8

考虑以下代码:

#include <vector>

struct A
{
    explicit A(int i_) : i(i_) {}
    int i;
};

int main()
{
    std::vector<int> ints;
    std::vector<A> As(ints.begin(), ints.end());
}

上面应该编译吗?我的感觉是它不应该,因为构造函数被标记explicit

Microsoft Visual C++ 同意,给出明确的错误信息:cannot convert from 'int' to 'const A'; Constructor for struct 'A' is declared 'explicit'

但是,使用Comeau 的在线编译器,代码编译成功。

哪个是对的?

编辑:

有趣的是,更改vectorset(在添加operator <到 A 之后)会导致两个编译器都给出错误。

但是,更改vector<int>tomap<int, int>vector<A>tomap<A, A>会导致两个编译器都接受代码!

4

6 回答 6

4

我查看了 GCC 的 STL 实现,它应该有类似的行为。这就是为什么。

  • a 的元素vector由一个通用函数模板初始化,该模板接受任何两种类型XV调用new( p ) X( v )where vis a V(我稍微解释一下)。这允许显式转换。
  • seta or的元素map由一个私有成员函数初始化,该私有成员函数_tree<T,…&gt;特别希望T const &传入 a。这个成员函数不是模板(除了是模板的成员),所以如果初始值不能被隐式转换to T,调用失败。(我再次简化了代码。)

该标准不要求在使用范围初始化容器时显式转换有效或隐式转换无效。它只是说范围被复制到容器中。对于您的目的绝对模棱两可。

令人惊讶的是,存在这样的模棱两可,考虑到他们已经考虑到几周前遇到的问题,已经改进了标准。

于 2010-01-05T05:10:09.317 回答
1

我认为这取决于std::vector<A> As(Iterator,Iterator)您在 STL 的特定实现中是如何实现的。

于 2010-01-05T00:16:57.773 回答
1

这是一个相当棘手的问题,可能是 VisualStudio 是对的,而 Comeau 是错的(这似乎很难相信)。

标准如果逐字阅读,根据复制构造函数定义向量构造函数(见引号),字面意思是通过取消引用迭代器获得的对象必须首先转换为类型 T 然后复制构造函数应该是叫。此时,使用显式构造函数的代码不应编译。

另一方面,期望一个实现直接调用一个将解引用的迭代器作为参数的构造函数似乎是合理的,在这种情况下,构造函数调用将是显式的,因此代码应该编译。这将与下面引用中的确切措辞背道而驰,因为复制构造函数是为给定类型 T 定义的,作为一个构造函数,该构造函数对 T 类型的对象进行单个可能的常量引用。

我想不出任何不使用 Comeau 方法的合理论据,并且我认为(这只是个人意见)是标准中关于向量构造函数复杂性的措辞可能应该重述为只需要 N 次调用适当的 T 构造函数,在适当的情况下,必须定义为与调用匹配的构造函数T( *first )(即,采用InputIterator::value_type(按值或可能的常量引用)的构造函数,或者从隐式转换InputIterator::value_type为 T后的 T 复制构造函数。

23.2.4.1 [lib.vector.cons]/1

复杂性:构造函数模板向量(InputIterator first, InputIterator last) 仅对 T 的复制构造函数进行 N 次调用(其中 N 是 first 和 last 之间的距离),如果 iterator first 和 last 是前向、双向或随机的,则不会重新分配访问类别。如果它们只是输入迭代器,它将对 T 的复制构造函数进行 N 次调用,并对日志 N 次重新分配进行排序。

我想知道 VS 编译器在给出时的行为:

struct T1;
struct T2 {
   operator T1 ();
};
struct T1 {
   T1( T2 const & ) { std::cout << "T1(T2)" << std::endl; }
};
T2::operator T1() {
   std::cout << "T2::operator T1" << std::endl;
   return T1(*this);
}
int main() {
   std::vector<T2> v2;
   v2.push_back( T2() );
   std::vector<T1> v1( v2.begin(), v2.end() );
}

使用 g++ 的结果是T2::operator T1不调用,而是v1直接从v2. 我假设使用 VS,编译器将使用T2::operator T1将每个元素转换v2为 T1 元素,然后调用复制构造函数。是这样吗?

于 2010-01-05T01:57:33.357 回答
1

这实际上归结为 STL 库如何实现的问题,而不是语言规范问题。语言规范中没有任何内容会禁止它工作,也没有任何东西要求它应该工作。

如果编写 stl::vector 构造函数来尝试使用赋值运算符进行隐式转换,那么它将失败。Microsoft STL 实现更有可能通过构造函数调用在初始化期间使用返回值优化,在这种情况下,此代码可以正常工作。

重要的是要注意,这样做的唯一原因是因为 stl::vector 构造函数是模板化的,唯一的要求是它是一个 input_iterator,或者更准确地说,它支持输入迭代器的所有必需功能。

我还想指出,这是一个很好的例子,说明为什么编写跨平台代码通常很困难。有时你会遇到两个编译器都不一定偏离语言标准的问题,但代码仍然不可移植。

于 2010-01-05T03:34:50.027 回答
0

此代码无法在 Comeau 中编译:

class Foo
{
public:
 explicit Foo(int bar)
 {
 }
};

class Bar
{
 void DoStuff(Foo foo){

 }
 void DoStuff2()
 {
  DoStuff(4);
 }
};

错误信息:

"ComeauTest.c", line 16: error: no suitable constructor exists to convert from "int"
          to "Foo"
    DoStuff(4);
            ^

1 error detected in the compilation of "ComeauTest.c".

因此,在线编译器基本支持显式构造函数。必须与向量/迭代器有关。

编辑这不过编译:

Foo foo = (Foo)5;

这是一个显式转换,所以没关系。我猜 Comeau 向量类在某个地方的构造函数中进行了显式转换,而微软的库没有。

更多关于显式构造函数 - http://www.glenmccl.com/tip_023.htm

于 2010-01-05T00:23:04.017 回答
0

是的,它应该编译。如果不使用构造函数,那么它的显式性就不是问题。

于 2010-01-05T00:37:12.520 回答