C++显式构造函数和迭代器
考虑以下代码:
#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());
}
上面的代码应该编译吗?我的感觉是不应该,因为构造函数被标记为“显式”。
Microsoft Visual C++ 同意这一点,并给出了明确的错误消息:无法从 'int' 转换为 'const A';结构“A”的构造函数被声明为“显式”
但是,使用 Comeau 的在线编译器,代码编译成功。
哪个是正确的?
编辑:
有趣的是,将vector
更改为set
(在将运算符<
添加到A之后)会导致两个编译器给出一个错误。
但是,将 vector
更改为 map
并将 vector
更改为 map
导致两个编译器都接受代码!
Consider the following code:
#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());
}
Should the above compile? My feeling is that it should not, due to the constructor being marked explicit
.
Microsoft Visual C++ agrees, giving a clear error message: cannot convert from 'int' to 'const A'; Constructor for struct 'A' is declared 'explicit'
However, using Comeau's online compiler, the code compiles successfully.
Which is correct?
Edit:
Interestingly, changing vector
to set
(after adding an operator <
to A) causes both compilers to give an error.
However, changing vector<int>
to map<int, int>
and vector<A>
to map<A, A>
causes both compilers to accept the code!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我查看了 GCC 的 STL 实现,它应该有类似的行为。原因如下。
vector
的元素由通用函数模板初始化,该模板接受任意两种类型X
和V
并调用new( p ) X ( v )
其中v
是一个V
(我解释了一下)。这允许显式转换。set
或map
的元素由_tree
的私有成员函数初始化,该函数特别期望T const &
传入。此成员函数不是模板(超出了模板的成员范围),因此如果初始值无法隐式转换为T
,调用失败。 (我再次简化了代码。)该标准不要求在初始化具有范围的容器时进行显式转换工作或隐式转换不起作用。它只是说范围被复制到容器中。对于您的目的来说绝对不明确。
令人惊讶的是,存在这样的歧义,考虑到他们已经如何考虑到诸如 几周前我拥有的那个。
I looked through GCC's STL implementation and it should have similar behavior. Here's why.
vector
are initialized by a generic function template which accepts any two typesX
andV
and callsnew( p ) X( v )
wherev
is aV
(I'm paraphrasing a bit). This allows explicit conversion.set
ormap
are initialized by a private member function of_tree<T,…>
which specifically expects aT const &
to be passed in. This member function isn't a template (beyond being a member of a template), so if the initial value can't be implicitly converted toT
, the call fails. (Again I'm simplifying the code.)The standard doesn't require that explicit conversion work or that implicit conversion not work when initializing a container with a range. It simply says that the range is copied into the container. Definitely ambiguous for your purpose.
Surprising such ambiguity exists, considering how they've already refined the standard in consideration of problems like the one I had a couple weeks ago.
我认为这取决于 std::vector 的方式。 As(Iterator,Iterator) 是在 STL 的特定实现中实现的。
I think it would depend on how
std::vector<A> As(Iterator,Iterator)
is implemented in your particular implementation of the STL.这是一个相当棘手的问题,可能 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
我想知道 VS 编译器在给出时的行为:
使用 g++ 时,结果是不调用
T2::operator T1
,而是构造v1
中的元素直接来自v2
中的元素。我假设使用 VS,编译器将使用T2::operator T1
将v2
中的每个元素转换为 T1 元素,然后调用复制构造函数。是这样吗?This is a rather tricky question, and it might be the case that VisualStudio is right and Comeau wrong (this seems really hard to believe).
The standard if read word by word, defines that vector constructor in terms of the copy constructor (see quote), and that literally means that the object obtained by dereferencing the iterator must first be converted into the type T and then the copy constructor should be called. At this point, with an explicit constructor the code should not compile.
It seems reasonable to expect an implementation, on the other hand, to directly call a constructor taking as argument the dereferenced iterator, in which case the constructor call would be explicit and thus the code should compile. This would go against the exact wording in the quote below, as the copy constructor is defined for a given type T as a constructor that taking a single possibly constant reference to an object of type T.
I cannot think of any reasonable argument not to use the Comeau approach, and my believe (this is just personal opinion) is that the wording in the standard with respect to the complexity of the vector constructor should probably be restated as requiring only N calls to the appropriate T constructor, where appropriate would have to be defined as the constructor that matches the call
T( *first )
(that is, either a constructor taking anInputIterator::value_type
(by value or possibly constant reference), or the T copy constructor after an implicit conversion fromInputIterator::value_type
to T.23.2.4.1 [lib.vector.cons]/1
I would like to know how the VS compiler behaves when given:
With g++ the result is that
T2::operator T1
is not called, but rather the elements inv1
are constructed directly from the elements inv2
. I would assume that with VS the compiler would useT2::operator T1
to convert from each element inv2
to a T1 element and then call the copy constructor. Is that so?这实际上归结为STL库如何实现的问题,而不是语言规范的问题。语言规范中没有任何内容会阻止它工作,也没有任何内容要求它工作。
如果编写 stl::vector 构造函数来尝试使用赋值运算符进行隐式转换,那么它将失败。 Microsoft STL 实现更有可能在初始化期间通过构造函数调用使用返回值优化,在这种情况下,此代码可以正常工作。
值得注意的是,它起作用的唯一原因是因为 stl::vector 构造函数是模板化的,唯一的要求是它是一个 input_iterator,或者更准确地说,它支持输入迭代器的所有必需功能。
我还想指出,这是一个很好的例子,说明了为什么编写跨平台代码通常很困难。有时,您最终会遇到这样的问题:两个编译器都不一定偏离语言标准,但代码仍然不可移植。
This really boils down to a question of how the STL library is implemented, not a language specification issue. There is nothing in the language spec that would prohibit this from working, nor is there anything that would require that it should work.
If the stl::vector constructor were written to try an implicit conversion using the assignment operator, then it would fail. It is more likely that the Microsoft STL implementation makes use of return-value optimization during initialization via a constructor call, in which case this code would work fine.
It is important to note that the only reason this works is because the stl::vector constructor is templated, and the only requirement is that it is an input_iterator, or more accurately that it supports all the required functionality of an input iterator.
I'd also like to point out that this is a prime example of why it is often difficult to write cross-platform code. Sometimes you end up with issues where neither compiler necessarily deviates from the language standard, but code still isn't portable.
此代码无法在 Comeau 中编译:
错误消息:
因此,在线编译器在基本级别上支持显式构造函数。一定与向量/迭代器有关。
编辑 然而,这会编译:
这是一个显式转换,所以没关系。我猜测 Comeau 向量类在构造函数中的某个地方进行了显式转换,而 Microsoft 的库则没有。
有关显式构造函数的更多信息 - http://www.glenmccl.com/tip_023.htm
This code does not compile in Comeau:
Error message:
So at the rudimentary level the online compiler supports explicit constructors. Must be something to do with the vector/iterators.
EDIT This however compiles:
Which is an explicit conversion, so that's OK. My guess the Comeau vector class does an explicit cast in the constructor somewhere, where's Microsoft's library doesn't.
More on explicit constructors - http://www.glenmccl.com/tip_023.htm
是的,它应该编译。如果不使用构造函数,那么它的显式性就不是问题。
Yes it should compile. If the constructor is not used, then its explicitness is not an issue.