数组初始值设定项的构造函数歧义

Constructor ambiguity with array initializer

我有一个问题,在使用 VS2017(C++14、C++17 和 ISO 最新版本)时,我无法将某个构造函数与数组初始值设定项一起使用。

我收到一个 C2397 conversion from 'double' to 'unsigned int' requires a narrowing conversion 错误,当它应该使用填充有单个元素的容器调用构造函数时。

#include <vector>

class obj
{
public:
    obj(const std::vector<double>& values, unsigned int stride)
        : values_(values), stride_(stride)
    {
    }

    obj(unsigned int m, unsigned int n)
        : stride_(n)
    {
    }

private:
    unsigned int stride_;
    std::vector<double> values_;
};

int main(int argc, char** argv)
{
    obj m(1, 1);                // correct constructor called.
    obj mm({ 42.0 }, 1);        // Error C2397

    return 0;
}

我可以通过显式声明容器来解决这个问题...

    obj mm(std::vector<double>({ 42.0 }), 1);

或者用多个项目初始化容器...

    obj mm({ 42.0, 12.0 }, 1);

后者显然没有用,而前者有点烦人,因为它是包含单个项目的容器的极端情况(尽管不是世界末日)。我认为这可能只对双打有问题(没有文字声明),但它甚至在用文字初始化浮点数时也会发生。即容器是 std::vector<float>,以下行仍然错误 C2397

    obj mm({ 42.0f }, 1);

我不相信编译器错误我自己没有遇到过很多(尽管它们显然存在),但我不禁认为这可能是一个,或者如果不是,是否有任何提及标准如何处理这种情况。理想情况下,我希望能够在不显式声明容器类型的情况下使用数组初始值设定项,因为当容器中存在多个项目时我可以这样做。这可能吗?

使用 {{}} 是所有情况下的解决方法

obj mm({{ 42.0 }}, 1); 

obj mm({{ 42.0, 12.0 }}, 1);

虽然第二种情况当然没有歧义(使用单大括号是在利用大括号省略)。

这个问题很好地介绍了主题:Brace elision in std::array initialization

你的意思是下面的吗

obj mm({ 1, 42.0 }, 1);

或以下

obj mm({ { 42.0 } }, 1);

可以向对象添加具有 std::initializer_list 的构造函数。

样本:

#include <iostream>
#include <vector>

struct Obj {
  std::vector<double> values;
  unsigned stride;

  Obj(std::initializer_list<double> values, unsigned stride = 1):
    values(values), stride(stride)
  {
    std::cout << "Obj::Obj(std::initializer_list<double>, unsigned)\n";
   }

  Obj(const std::vector<double> &values, unsigned stride = 1):
    values(values), stride(stride)
  {
    std::cout << "Obj::Obj(const std::vector<double>&, unsigned)\n";
  }

  Obj(unsigned m, unsigned stride = 1):
    stride(stride)
  {
    std::cout << "Obj::Obj(unsigned, unsigned)\n";
  }
};

int main()
{
  Obj mm({ 42.0f }, 1);
  Obj mm1(1, 1);
  Obj mm2(std::vector<double>({ 42.0 }), 1);
  Obj mm3({ 42.0, 12.0 }, 1);
  Obj mm4(std::vector<double>{ 42.0 }, 1);
}

输出:

Obj::Obj(std::initializer_list<double>, unsigned)
Obj::Obj(unsigned, unsigned)
Obj::Obj(const std::vector<double>&, unsigned)
Obj::Obj(std::initializer_list<double>, unsigned)
Obj::Obj(const std::vector<double>&, unsigned)

Live Demo on coliru