【问题标题】:How to cause a compile time error based on the size of an initializer_list?如何根据 initializer_list 的大小导致编译时错误?
【发布时间】:2015-03-14 22:58:08
【问题描述】:

如果我有一个像这样的简单结构

struct Point { int x, y; };

那我就可以了

int main()
{
    Point p1 = { 10 };          // x = 10, y = 0
    Point p2 = { 10, 20 };      // x = 10, y = 20
    Point p3 = { 10, 20, 30 };  // Compile error: too many initializers for ‘Point’
    return 0;
}

我现在希望在初始化 Point 时具有相同的行为,Point 成为一个类,但 xy 成为私有并使用访问器等。

我的第一次尝试是

class Point
{
public:
    Point( std::initializer_list<int> init )
    {
        switch( init.size() )
        {
            case 0: x = 0; y = 0; break;
            case 1:
            {
                auto iter = init.begin();
                x = *iter++; y = 0;
                break;
            }
            case 2:
            {
                auto iter = init.begin();
                x = *iter++; y = *iter++;
                break;
            }
            default:
                throw 0;
                break;
        }
    }
private:
    int x, y;

};

这有点工作,但将编译时错误更改为运行时错误。现在的问题是:如何使这个类的行为与简单结构相同,即当初始化列表太大时导致编译时错误?

环顾四周,我发现

通读答案和 cmets,我了解了一些 constexprstatic_assert 问题,但仍然无法找到解决我问题的方法。是否有可能在 C++11(或 C++14)中导致编译时错误?编译器肯定知道它需要做的所有事情,而且对于有人想要做的事情来说,标准可能会以某种方式将其引入。

【问题讨论】:

  • 为什么使用 std::initializer_list 来初始化非容器成员?在您的情况下是否有必要从列表/结构文字初始化结构?
  • 上面的代码是一个简单的玩具示例,试图说明“如何根据 initializer_list 的大小在编译时出错?”这个问题。实际使用是/将在由少量(基于目标硬件平台已知的编译时间)内存支持的容器类中。然后在编译时使用大括号初始化器填充这些内存块。代码正在从类 C 接口转换为 C++ 风格。可悲的是,我无法分享实际代码:(

标签: c++ c++11 c++14


【解决方案1】:

通过将初始化列表构造函数替换为类似以下的构造函数...

Point(int x, int y) :m_x(x), m_y(y) {}

注意,我将私有变量 x 和 y 重写为 m_x 和 m_y。

现在,当您尝试使用超过 2 个参数初始化 Point 对象时,您将收到一个编译器错误,类似于您在 Point 是一个结构时遇到的错误。

【讨论】:

  • 谢谢,但这会破坏用于初始化对象的初始化列表语法。我想写Point p = { 10, 20 } 而不是Point p( 10, 20 ),因为您的解决方案需要我这样做。
  • 你为什么要那个?
  • @user2746401 你也可以用这个构造函数写Point p = { 10, 20 }
  • @Axalo 有很多代码已经使用了大括号初始化语法,我不想在从结构体转移到类时破坏它。
  • @AntonSavin 好点。我担心单参数情况,即Point p = { 10 },但这很好,因为我可以为构造函数的y 参数使用默认值。
【解决方案2】:

没有编译时方法可以检索std::initializer_list 大小,但您不需要它。

类似于初始化器的语法被命名为统一初始化,是c++11的一部分,统一命名为自定义语法。

这是一个适用于您的 Point 的完整示例,如您所见,带参数的构造函数可以匹配初始化列表。

#include <iostream>

class Point {
public:
    Point() = default;

    Point( int x, int y ) : x_{x}, y_{y} {}
    Point( int x ) : x_(x) {}

    // or in your case, you can use a default argument
    //Point( int x, int y = int{} ) : x_{x}, y_{y} {}

    int X() const { return x_; }
    int Y() const { return y_; }
private:
    int x_{};
    int y_{};
};

void DisplayPoint( Point const & p) {
    std::cout << "( " << p.X() << ", " << p.Y() << " )" << std::endl;
}

Point GetAPoint() {
    return { 3, 5 };
}

int main() {
    DisplayPoint( {} );
    DisplayPoint( { 1 } );
    DisplayPoint( { 1, 2 } );
    DisplayPoint( GetAPoint() );
    DisplayPoint( Point( 5, 3 ) ); // pre c++11
    DisplayPoint( Point{ 5, 3 } ); // in the case the constructor is explicit
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-28
    • 2019-07-21
    • 2019-08-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多