【问题标题】:Convert class<T> to class<U> if T is convertible to U如果 T 可转换为 U,则将 class<T> 转换为 class<U>
【发布时间】:2016-12-12 21:35:17
【问题描述】:

我想创建一个模板类test&lt;T&gt;,如果T 可转换为U,我可以将其转换为test&lt;U&gt;(可能是隐式的)。我想到的最简单的想法是添加一个带有test&lt;U&gt; 的模板构造函数,其中模板参数Uenable_if 控制。

#include <iostream>
#include <type_traits>

template <typename T>
class test {
    int _foo;
public:
    int foo() const { return _foo; }

    test() : _foo(5) {}

    // This works:

    // template <typename U>
    // test(test<U> other)
    // : _foo(other.foo()) {}

    // This doesn't, can't resolve `U`:

    template <typename U>
    test(test<typename std::enable_if<std::is_convertible<U, T>::value, U>::type> other)
    : _foo(other.foo()) {}

};

int main() {
    test<int> a;
    test<long> b = a; // T = long, U = int

    // true
    std::cout << std::boolalpha << std::is_convertible<int, long>::value << std::endl;

    return 0;
}

如果我只声明第一个模板化构造函数,代码就可以正常工作。使用第二个构造函数,它不会编译:

21:9: note: candidate template ignored: couldn't infer template argument 'U'
    test(test<typename std::enable_if<std::is_convertible<U, T>::value, U>::type> other)
    ^

为什么在这种情况下编译器不能推断U 看起来很简单,我一定是在模板推导中遗漏了什么。此外,如果无法以这种方式实现转换,test&lt;T&gt; 转换为test&lt;U&gt; 的最佳方法是什么?


作为旁注,我设法通过让所有test&lt;T&gt; 成为朋友,并声明一个在实现中使用enable_if 的转换运算符(如下所示)来使其工作,但我仍然想知道为什么首先,更简单的方法行不通。

template <typename T>
class test {
    int _foo;

    template <typename U>
    friend class test;

    test(int foo) : _foo(foo) {}

public:
    int foo() const { return _foo; }

    test() : _foo(5) {}

    template <typename U>
    operator test<U>() const {
        using return_t = typename std::enable_if<std::is_convertible<U, T>::value, U>::type;
        return test<return_t>(_foo);
    }
};

【问题讨论】:

标签: c++ templates constructor implicit-conversion sfinae


【解决方案1】:

U 出现在 non-deduced context 中。

这解决了编译器错误:

template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value, U>::type>
test(test<U> other)
: _foo(other.foo()) {}

live example

【讨论】:

    【解决方案2】:

    为什么在这种情况下编译器不能推断出 U?

    类型推导根本无法从a&lt;T&gt;::b等参数类型推断出T。即使你有

    template <typename T> struct identity { typedef T type; };
    

    那么编译器仍然不能排除你在某处提供一个专门化,偷偷地使identity&lt;X&gt;::typeY为某些类型X

    std::enable_if 也一样:标准库类模板在语言规则中没有得到特殊处理,所以编译器无法判断如果std::enable_if&lt;cond, U&gt;::type 应该是X,那么U 有成为X

    这就是为什么对于常规函数,std::enable_if 通常出现在返回类型中。它不能在参数中,原因与构造函数相同。它不能在模板参数中,因为调用者可以指定不同的类型并绕过您的限制。但是返回类型是安全的。

    构造函数没有返回类型。幸运的是,调用者无法显式指定构造函数模板参数,因此将 std::enable_if 放入模板默认参数中,正如 m.s. 已经回答的那样。那里很安全。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-06
      相关资源
      最近更新 更多