【发布时间】:2014-02-17 18:05:41
【问题描述】:
给定以下代码:
#include <iostream>
#include <vector>
template <typename Source>
class ConvertProxy
{
Source const* m_source;
public:
ConvertProxy( Source const& source )
: m_source( &source )
{
}
template <typename Dest>
operator Dest() const
{
return Dest(m_source->begin(), m_source->end());
}
};
template <typename Source>
ConvertProxy<Source> convert( Source const& source )
{
return ConvertProxy<Source>( source );
}
int
main()
{
std::vector<int> src;
for ( int i = 0; i != 5; ++ i ) {
src.push_back( i );
}
std::vector<double> dest = convert( src ); /* XXX */
for ( std::vector<double>::const_iterator i = dest.begin(), e = dest.end();
i != e;
++ i ) {
std::cout << *i << std::endl;
}
return 0;
}
这在 C++11 中是否合法,还是标记为 XXX 的行不明确?
同样的问题,但标记的行替换为:
std::vector<double> dest( convert( src ) );
或与
std::vector<double> dest;
dest = convert( src );
在 C++11 之前,我认为第二个是非法的,但其他两个 绝对不是。
FWIW:g++ (4.8.2) 接受第一个,但不接受其他两个(带有
-std=c++11;否则它接受第一个和第三个,但是
不是第二个)。 VS 2013 接受所有这些,但 Intellisense
标记所有这些都是错误的(这就是触发我的
兴趣:滚动条上有一个漂亮的红色标记,带有
红色下划线的符号,但代码编译完美)。
换句话说:三种编译器,三种不同的行为。
(对于那些想知道为什么的人:这是一个标准的成语 用于获取上下文——作业的左侧, 例如——参与重载决议。)
【问题讨论】:
-
由于most vexing parse 问题,您的第一个替换
std::vector<double> dest(convert(src));可能不明确。 -
@CouchDeveloper 对于最棘手的解析问题,
convert必须命名一个类型。它没有。我认为对于任何具有多个可以用单个参数调用的构造函数的类型,第一个替换(第二个版本)是(并且一直是)模棱两可的。无论如何,Visual Studios 2013 接受它(并调用我想要的转换)。我正在寻找可以向我解释新的initializer_list构造函数如何干预的人。一些解释 WRT rvalue-ref ctor 也会很好,尽管我想我理解它们。 -
我唯一能看到
initializer_list被使用的地方是在返回值中:你可以写ConvertProxy<Source>(source)而不是{source},但那是因为你知道返回的类型。同样对于这个特殊问题,ConvertProxy 的意义何在,为什么不只使用一个带有 typename 目标的转换函数。 -
如果你不介意使用自动,你可以实现更简单的事情。下面是代码:ideone.com/WietLn,它利用了 initializer_list、r 值引用和基于范围的循环。编辑:您可以更改对 r-value refs 的引用以返回 convert 但我没有看到任何需要。