可能在G ++ 6.1.0中回归

Bul*_*net 22 c++ c++11

以下代码

#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,复制列表初始化的规则:

[...]包括所有构造函数,但声明如果通过重载决策选择了显式构造函数,程序就会形成错误.[...]

[over.match.list]:

[...]在复制列表初始化中,如果explicit选择了构造函数,则初始化是错误的.[ 注意:这与其他情况([over.match.ctor],[over.match.copy])不同,其中只考虑转换构造函数进行复制初始化.此限制仅适用于此初始化是重载解析的最终结果的一部分. - 结束说明 ]

因此,您的程序被正确地考虑(在目前的标准下)是不明确的.

进一步阅读:如果copy-list-initialization允许显式构造函数,会出现什么问题?

  • 我不确定这肯定是一个缺陷,尽管有点令人惊讶. (2认同)
  • "没有实施CWG 1228"有什么要实施的?我认为CWG的结果是标准中写的规则是正确的.因此没有提议的改变,对吗? (2认同)