首页 > 解决方案 > 当构造函数输入包含重复项时创建编译器错误

问题描述

考虑以下类:

class Base {
public:
    Base(const std::initializer_list<const char*>& words) 
        : words_(words) {}
    std::initializer_list<const char*> words_;
};

class Derived_OK : public Base
{
public:
    Derived_OK()
        : Base({ "dog", "car", "time"}){}

};

我想通过创建编译时错误来禁止来自基类的派生类,其中初始化列表包含重复项。例如,不应允许以下类:

class Derived_BAD : public Base
{
public:
    Derived_BAD()
        : Base({ "dog", "car", "time", "car"}){} // do not want to allow duplicates at compile time

};

我最初的方法是尝试模板化 Base 类。但是,据我确定,我不能使用非类型模板参数,即使在可以将字符串作为参数传递的 C++20 中(我相信在 C++20 方法中您只能传递一个字符串)。

我的下一个方法是编写一个 constexpr 函数来确定单词是否唯一

constexpr bool unique_words(const std::initializer_list<const char*>& words);

然后重写Base类如下:

class Base {
public:
    constexpr Base(const std::initializer_list<const char*>& words) 
        : words_(words)
    {
        static_assert(unique_words(words));
    }
    std::initializer_list<const char*> words_;
};

尽管此函数在类外部工作,但在 Base 构造函数内部,编译器告诉我不能将构造函数参数的值用作常量。当然,我可以编写一个运行时检查,但我真的想阻止在编译时在初始化列表中创建重复的单词。这可能吗?

标签: c++compilationmetaprogramming

解决方案


要检查任何内容,您必须 constexpr 构造该类。要在 constexpr 函数中触发编译时错误,您可以抛出,请参阅Generate compile-time error if compile-time-constant parameter is wrong

#include <initializer_list>
#include <type_traits>
#include <array>
#include <stdexcept>

template<std::size_t N>
constexpr bool unique_words(const std::array<const char*, N>& words) {
    // TODO: implement real logic here
    return words[0][0] == 'd';
}

template<std::size_t N>
struct Base {
    constexpr Base(const std::array<const char*, N>& words) 
        : words_(words)
    {
        if (!unique_words<N>(words)) {
             throw std::invalid_argument("Base must take unique words!");
        }
    }
    std::array<const char*, N> words_;
};


struct Derived_BAD : public Base<4> {
    constexpr Derived_BAD() : Base{{"e", "car", "time", "car"}} {}
};

int main() {
    constexpr Derived_BAD var; // compile-time error - can't throw in constexpr
    Derived_BAD var2; // will throw at runtime
}

不要存储std::initializer_list在你的课堂上!!请参阅使用 std::initializer_list 作为成员变量

构造时无法进行编译时检查constexpr。如果您真的想检查这种情况,您可以使用 GNU__builtin_constant_p扩展并启用编译器优化,请参阅 GNU 文档和启用静态检查以进行常量评估如何在常量评估表达式中获得编译时错误?.


推荐阅读