如果 T 可转换为 U,则将 class<T> 转换为 class<U>

Convert class<T> to class<U> if T is convertible to U

我想创建一个模板化的 class test<T>,如果 T 可转换为 U,我可以将其转换为 test<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);
    }
};

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

Why can't the compiler infer U in this case?

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

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

那么编译器 仍然 不能排除你在某个地方提供了一个专门化的地方,偷偷地为某种类型 X identity<X>::type Y

std::enable_if相同:标准库class模板在语言规则中没有得到特殊处理,所以编译器无法判断std::enable_if<cond, U>::type是否应该是 X,那么 U 必须是 X

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

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