以下代码
#include <string>
#include <map>
#include <cassert>
struct AV {
explicit AV(std::string const&) {}
};
#if 1
static void check_cache_item(
std::map<std::string, std::string> const& items) // FIXME remove
{
assert(!items.empty());
}
#endif
static void check_cache_item(
std::map<std::string, AV> const& items)
{
assert(!items.empty());
}
int main()
{
check_cache_item({ { "id", "0" }, { "pk", "#0" } });
check_cache_item({ { "id", "0" }, { "pk", "#1" } });
check_cache_item({ { "id", AV{"0"} }, { "pk", AV{"#1"} } });
}
Run Code Online (Sandbox Code Playgroud)
被g ++ 4.8.4,g ++ 5.3.0,clang ++ 3.9.0接受; 但是g ++ 6.1.0给出了一个错误:
cci.cc: In function ‘int main()’:
cci.cc:25:55: error: call of overloaded ‘check_cache_item(<brace-enclosed initializer list>)’ is ambiguous
check_cache_item({ { "id", "0" }, { "pk", "#0" } });
^
cci.cc:10:17: note: candidate: void check_cache_item(const std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&)
static void check_cache_item(
^~~~~~~~~~~~~~~~
cci.cc:16:17: note: candidate: void check_cache_item(const std::map<std::__cxx11::basic_string<char>, AV>&)
static void check_cache_item(
^~~~~~~~~~~~~~~~
cci.cc:26:55: error: call of overloaded ‘check_cache_item(<brace-enclosed initializer list>)’ is ambiguous
check_cache_item({ { "id", "0" }, { "pk", "#1" } });
^
cci.cc:10:17: note: candidate: void check_cache_item(const std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&)
static void check_cache_item(
^~~~~~~~~~~~~~~~
cci.cc:16:17: note: candidate: void check_cache_item(const std::map<std::__cxx11::basic_string<char>, AV>&)
static void check_cache_item(
^~~~~~~~~~~~~~~~
cci.cc: At global scope:
cci.cc:10:17: warning: ‘void check_cache_item(const std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&)’ defined but not used [-Wunused-function]
static void check_cache_item(
^~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)
如果我#ifdef第一个构造函数,那么每个编译器都会抛出一个错误(正确的,因为AV构造函数是显式的).
这是G ++ 6.1.0中的回归吗?
eca*_*mur 14
这是标准的一个令人惊讶和有些不幸的方面(我甚至称之为缺陷); 它是复制列表初始化的重载解析规则(CWG 1228中确认的[over.match.list])与(根据n4387)的元素转发构造函数之间的冲突的结果.pair
gcc(> = 6.1.0)拒绝你的程序是正确的; clang接受它是不正确的.早期版本的gcc接受你的程序,因为他们还没有实现n4387; 铛接受你的计划,因为它排除考虑明确的构造函数重载的副本列表初始化,违反[over.match.list]根据标准(调用带有支撑,初始化列表中的一个显式构造:含糊不清或不?)
如果我们剥离程序中无关紧要的方面,那么可以归结为一个简单的重载决策问题:
struct A { explicit A(int, int); };
struct B { B(int, int); };
void f(A);
void f(B);
int main() { f({0, 0}); }
Run Code Online (Sandbox Code Playgroud)
这A是站在一起,pair<std::string const, AV>并B代表着pair<string const, string>.构造函数A是显式的,因为它涉及n4387的显式构造函数AV; 但根据CWG 1228,复制列表初始化的规则:
[...]包括所有构造函数,但声明如果通过重载决策选择了显式构造函数,程序就会形成错误.[...]
[...]在复制列表初始化中,如果
explicit选择了构造函数,则初始化是错误的.[ 注意:这与其他情况([over.match.ctor],[over.match.copy])不同,其中只考虑转换构造函数进行复制初始化.此限制仅适用于此初始化是重载解析的最终结果的一部分. - 结束说明 ]
因此,您的程序被正确地考虑(在目前的标准下)是不明确的.
进一步阅读:如果copy-list-initialization允许显式构造函数,会出现什么问题?