模板类型之间的隐式转换

5
我创建了两种类型:bool_tnumber_t,我想将其中一种转换为另一种(双向均可)。然而,我在将bool_t转换为number_t时遇到了一些问题。

基本上我想做的是这样的(但它不能编译):

template<bool v>
struct bool_t {
    template<template<int> typename T>
    operator T<v ? 1 : 0>() {
        return {};
    }
};

template<int N>
struct number_t {
template<int n1, int n2>
friend number_t<n1 + n2> operator+(number_t<n1>, number_t<n2>) {
  return {};
}
};


int main() {
    number_t<0>{} + bool_t<0>{};   
}

错误信息如下:

prog.cc:19:19: error: invalid operands to binary expression ('number_t<0>' and 'bool_t<0>')
    number_t<0>{} + bool_t<0>{};   
    ~~~~~~~~~~~~~ ^ ~~~~~~~~~~~
prog.cc:12:26: note: candidate template ignored: could not match 'number_t' against 'bool_t'
friend number_t<n1 + n2> operator+(number_t<n1>, number_t<n2>) {
                         ^
1 error generated.

如何解决这个问题?

1
同一个函数参数中不能同时拥有用户定义的转换和模板参数推导。 - aschepler
那么解决这个问题的唯一方法就是使用类似于 bool_to_number 的函数吗? - Antoine Morrier
或许还需要更多的 operator+ 定义。但是,如果我定义了 template <int> class Thing;,你真的希望 bool_t<true>{} 在不知道更多信息的情况下隐式转换为 Thing<1>{} 吗? - aschepler
如果这是唯一的转换,为什么要使用模板转换运算符,而不是只使用 template<bool v> struct bool_t { operator number_t<v ? 1 : 0>() const; };?根据实际需要的泛化类型,可能还有其他合理的解决方案。 - aschepler
它也不能编译:https://wandbox.org/permlink/jJMPX51yrcI2etsY。我想我应该写一个函数来处理数字和布尔值^^... - Antoine Morrier
显示剩余4条评论
2个回答

3

当尝试使用模板参数推导将函数参数类型与函数参数类型匹配时,不会考虑用户定义的转换。因此,这个问题是这种错误的一个更加复杂的版本:

template <int> struct X {};
struct Y {
    operator X<2> () const { return {}; }
};
template <int N>
void f(X<N>) {}

int main()
{
    Y y;
    f(y); // Error: Cannot deduce template argument.
}

由于您似乎正在开发一个类似于模板元编程库的东西,也许您可以在您的库中定义一种自定义机制,将类型转换为“模板”?

#include <type_traits>

template<bool v>
struct bool_t {
    // (Add some appropriate SFINAE.)
    template<template<int> typename T>
    constexpr T<v ? 1 : 0> convert_template() const {
        return {};
    }
};

template<typename T, template<int> class TT>
struct type_specializes : std::false_type {};

template<template<int> class TT, int N>
struct type_specializes<TT<N>, TT> : std::true_type {};

template<int N>
struct number_t {
    // Allow "conversion" to my own template:
    template<template<int> typename T>
    constexpr std::enable_if_t<type_specializes<number_t, T>::value, number_t>
    convert_template() const { return {}; }

private:
    // Used only in decltype; no definition needed.
    template<int n1, int n2>
    static number_t<n1 + n2> sum_impl(number_t<n1>, number_t<n2>);

    template<typename T1, typename T2>
    friend auto operator+(T1&& x, T2&& y)
        -> decltype(number_t::sum_impl(
             x.template convert_template<number_t>(),
             y.template convert_template<number_t>()))
    { return {}; }
};

int main() {
    number_t<0>{} + bool_t<0>{};   
}

如果要允许两个操作数都是bool_t特化类型,那么operator+需要成为一个适当的可见命名空间成员;如果合适的话,可以添加更多的SFINAE检查。

我真的很喜欢这种方法。但是,由于您正在使用带有完整模板化运算符+的friend,对于其他类型来说并不危险,对吗?(因为ADL)? - Antoine Morrier
@AntoineMorrier ADL将限制表达式,其中某些number_t<N>是参数的关联类,是的。但它也在做SFINAE:由于返回类型本质上是decltype(sum_impl(...)),因此只有当两个参数都具有convert_template成员模板时,此operator+才会产生候选项。(也许我应该使用number_t::sum_impl,以避免在那里对sum_impl进行ADL。) - aschepler
你确定在这种情况下SFINAE起作用吗?(我的意思是没有使用decltype) - Antoine Morrier
1
@AntoineMorrier 抱歉,SFINAE在占位符返回类型上不起作用。(我做了一个测试,但犯了一个错误。)https://timsong-cpp.github.io/cppwp/dcl.spec.auto#11“从这个实例化中产生的任何错误都不在函数类型的上下文中,并且可能导致程序不合法。” - aschepler
1
修正了示例。 - aschepler

2
你的问题归结为语言只允许最多一次隐式转换。一个解决方案是编写一个接受bool_t参数的函数:

最初的回答

你的问题在于该语言最多允许一次隐式转换。因此,一种解决方法是编写一个接受bool_t参数的函数:

number_t<n1 + n2> operator+(number_t<n1>, bool_t<n2>) {
  //return something
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接