c ++模板类型推导在强制转换运算符中失败

时间:2017-10-06 15:05:06

标签: c++ templates casting template-meta-programming type-deduction

我已经简化了一些更困难的问题:

http://coliru.stacked-crooked.com/a/2660b33492651e92

#include <iostream>
#include <string>
#include <type_traits>

template<typename C>
struct get_type
{
    C operator()() const = delete;
};

template<>
struct get_type<std::string>
{
    std::string operator()() const { return "asd"; }
};

template<>
struct get_type<size_t> {
    size_t operator()() const { return 6; }
};

struct S
{
    S(){}
    template<typename T>
    operator T() { return get_type<T>{}(); }
};

struct A
{
    A() :s{S{}}, n{S{}} {}
    std::string s;
    size_t n;
};

int main()
{
    A a;
    std::cout << "Spock out." << std::endl;
}

这会产生以下错误:

'In instantiation of 'S::operator T() [with T = char]':'...

为什么T被推导为char而不是std :: string?

修改

@ YSC的答案似乎是正确的: https://stackoverflow.com/a/46608866/4723722

我编辑了帖子以添加解决方案: http://coliru.stacked-crooked.com/a/06d31d981acd2544

struct S
{
    S(){}
    template<typename T>
    explicit operator T() { return get_type<T>{}(); }
};

2 个答案:

答案 0 :(得分:6)

下面:

A() : s( S{} ), ...

来自A::s实例的S的构造不明确,因为对于女巫T的每个类型std::is_constructible<std::string, T>,模板函数S::operator T()都是可能的转换从STstd::string的路径。

通过使用T = char进行测试,它会启动您的GCC版本。 clang列出多个候选人:http://coliru.stacked-crooked.com/a/17e247cca8b79c77

/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/basic_string.h:413:7: note: candidate constructor
      basic_string(const _Alloc& __a) _GLIBCXX_NOEXCEPT
      ^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/basic_string.h:421:7: note: candidate constructor
      basic_string(const basic_string& __str)
      ^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/basic_string.h:493:7: note: candidate constructor
      basic_string(const _CharT* __s, const _Alloc& __a = _Alloc())
      ^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/basic_string.h:515:7: note: candidate constructor
      basic_string(basic_string&& __str) noexcept
      ^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/basic_string.h:542:7: note: candidate constructor
      basic_string(initializer_list<_CharT> __l, const _Alloc& __a = _Alloc())

正如OP自己发现的那样,使S explicit的转换运算符解决了歧义(demo):

struct S
{
    S(){}
    template<typename T>
    explicit operator T() { return get_type<T>{}(); }
};

这是有效的(正如用户AndyG发现的那样)因为在[over.match.copy]下可以读到:

  

初始化临时绑定到构造函数的第一个参数时,其中参数的类型为“引用可能的cv-qualified T”,并且在直接初始化的上下文中使用单个参数调用构造函数“cv2 T”类型的对象,也考虑显式转换函数。

答案 1 :(得分:4)

下面:

A() : s { S{} }, ...

您正在使用braced-init-list初始化sstd::stringstd::initializer_list<char>的构造函数,因此首先考虑这一点。这成功,导致调用S::operator char,这是一个格式错误,因为它包含对已删除函数的调用。

当您将explicit关键字添加到转换函数时,这会使initializer_list的构造函数不可行,因此编译器必须考虑其他构造函数。其中,复制构造函数是唯一可行的,由于@YSC给出的原因:在直接初始化的上下文中考虑复制构造函数时允许使用显式转换函数。