【问题标题】:c++ template type deduction fail in cast operatorc ++模板类型推导在强制转换运算符中失败
【发布时间】:2018-03-18 10:29:32
【问题描述】:

我已将我更困难的问题简化为:

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>{}(); }
};

【问题讨论】:

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


    【解决方案1】:

    这里:

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

    S 实例构造A::s 是不明确的,因为对于女巫std::is_constructible&lt;std::string, T&gt; 的每种类型T,模板函数S::operator T() 是从ST 到的可能转换路径std::string.

    它会从测试T = char 开始连接您的 GCC 版本。 clang list 多个候选人: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自己发现的,制作Sexplicit的转换运算符解决了歧义(demo):

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

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

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

    【讨论】:

    • 如果将return get_type&lt;T&gt;{}(); 替换为return {};,GCC 实际上会编译代码。我猜 GCC 在这种情况下会使列表构造函数变得贪婪,而 Clang 不会。
    • @chris 看起来像是 gcc 的不当行为。
    • 哦,我也刚刚意识到 Coliru 链接有 : s(...) 而问题有 : s{...} :( 如果你使用 : s{...},Clang 也会用我上面的更改编译它。
    • 天哪,你是对的 coliru.stacked-crooked.com/a/06d31d981acd2544 如果我明确表示运算符 T,它会编译
    • 整洁。我想知道为什么要考虑显式转换运算符,但它就在[over.match.copy]下的标准中:当初始化一个临时绑定到构造函数的第一个参数时,参数类型为“reference”到可能 cv 限定的 T”,并且在直接初始化类型为“cv2 T”的对象的上下文中使用单个参数调用构造函数,还考虑了显式转换函数。编辑:或者 [over.match.conv] 下的类似引用可能更好。
    【解决方案2】:

    这里:

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

    您正在使用花括号初始化列表初始化s,而std::string 具有来自std::initializer_list&lt;char&gt; 的构造函数,因此首先考虑这一点。这成功了,导致调用S::operator char,这是错误的,因为它包含对已删除函数的调用。

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

    【讨论】:

      猜你喜欢
      • 2015-07-22
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-21
      • 1970-01-01
      • 2012-01-08
      相关资源
      最近更新 更多