请考虑以下代码:
#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's online compiler,代码会成功编译。
哪个是对的?
修改
有趣的是,将vector
更改为set
(向A添加operator <
后)会导致两个编译器都出错。
但是,将vector<int>
更改为map<int, int>
并将vector<A>
更改为map<A, A>
会导致两个编译器都接受该代码!
答案 0 :(得分:3)
我查看了GCC的STL实现,它应该有类似的行为。这就是原因。
vector
的元素由通用函数模板初始化,该模板接受任意两种类型X
和V
,并调用new( p ) X( v )
v
为{ {1}}(我有点解释)。这允许显式转换。V
或set
的元素由map
的私有成员函数初始化,该函数特别期望传递_tree<T,…>
。此成员函数不是模板(不是模板的成员),因此如果初始值无法隐式转换为T const &
,则调用失败。 (我再次简化了代码。)当初始化具有范围的容器时,标准不要求显式转换工作或隐式转换不起作用。它只是说范围被复制到容器中。对你的目的一定是模棱两可的。
考虑到几周前the one I had等问题,他们已经完善了标准,因此存在令人惊讶的模糊性。
答案 1 :(得分:1)
我认为这将取决于您在STL的特定实现中如何实现std::vector<A> As(Iterator,Iterator)
。
答案 2 :(得分:1)
这是一个相当棘手的问题,可能是VisualStudio是正确的并且Comeau错误(这似乎很难相信)。
如果逐字阅读标准,则根据复制构造函数(参见quote)定义该向量构造函数,并且字面意思是通过解除引用迭代器获得的对象必须首先转换为应该调用类型T,然后调用复制构造函数。此时,使用显式构造函数,代码不应该编译。
另一方面,期望实现直接调用构造函数作为参数的解引用迭代器似乎是合理的,在这种情况下构造函数调用将是显式的,因此代码应该编译。这将违反下面引文中的确切措辞,因为复制构造函数是为给定类型T定义的构造函数,它对类型为T的对象采用单个可能的常量引用。
我想不出任何合理的论据,不使用Comeau方法,而我相信(这只是个人观点)是标准中关于向量构造函数的复杂性的措辞应该重述为要求< em>只有N个调用相应的T构造函数,必须将其定义为与调用T( *first )
匹配的构造函数(即,构造函数采用InputIterator::value_type
(通过值或可能是常量引用),或从InputIterator::value_type
到T的隐式转换后的T拷贝构造函数。
23.2.4.1 [lib.vector.cons] / 1
复杂性:构造函数模板向量(InputIterator 首先,InputIterator last)仅限 N调用T的复制构造函数 (其中N是两者之间的距离 第一个和最后一个)并没有重新分配 如果迭代器的第一个和最后一个是 前向,双向或随机 访问类别。它使订单N 调用T和的复制构造函数 订单日志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元素,然后调用复制构造函数。是这样吗?
答案 3 :(得分:1)
这实际上归结为如何实现STL库的问题,而不是语言规范问题。语言规范中没有任何内容可以禁止它工作,也没有任何东西需要它可以工作。
如果编写stl :: vector构造函数以使用赋值运算符尝试隐式转换,那么它将失败。 Microsoft STL实现更有可能在初始化期间通过构造函数调用使用返回值优化,在这种情况下,此代码可以正常工作。
重要的是要注意,这个工作的唯一原因是因为stl :: vector构造函数是模板化的,唯一的要求是它是一个input_iterator,或者更准确地说它支持输入迭代器的所有必需功能
我还想指出,这是一个很好的例子,说明为什么编写跨平台代码通常很困难。有时你会遇到这样的问题:编译器都不一定会偏离语言标准,但代码仍然不可移植。
答案 4 :(得分: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".
因此,在最基本的层面上,在线编译器支持显式构造函数。必须与vector / iterators有关。
编辑但是这会编译:
Foo foo = (Foo)5;
这是一个显式转换,所以没关系。我的猜测Comeau向量类在某个地方的构造函数中进行了显式转换,其中Microsoft的库没有。
有关显式构造函数的更多信息 - http://www.glenmccl.com/tip_023.htm
答案 5 :(得分:0)
是的,应该编译。如果没有使用构造函数,那么它的显式性不是问题。