如果T可转换为U

时间:2016-08-06 16:58:36

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

我想创建一个模板化的类test<T>,如果test<U>可以转换为T,我可以将其转换为U(可能隐式)。我想到的最简单的想法是添加一个带有test<U>的模板化构造函数,其中模板参数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<T>转换为test<U>的最佳方法是什么?

作为旁注,我设法通过制作所有test<T>朋友并声明在实现中使用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);
    }
};

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:2)

  

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

类型扣除从根本上无法从T等参数类型推断a<T>::b。即使你有

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

然后编译器仍然不能排除你在某个类型identity<X>::type偷偷地制作Y X的地方提供专业化。

std::enable_if相同:标准库类模板在语言规则中没有得到特殊处理,因此编译器无法弄清楚如果std::enable_if<cond, U>::type应该是是X,然后U必须是X

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

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