C++模板类中关于用户定义运算符的隐式转换

6

我有一个结构模板A<x>和一个与int相加的+运算符。

#include <iostream>
template<int x>
struct A{
    int a;  
};
template<int x>
int operator+(A<x> a, int b){
    return a.a+b;
}

我创建了一个结构体模板 B<x>,可以转换为 A<x>
template<int x>
struct B{
    int b=3;
    operator A<x>(){
        return {b+10};
    }
};

现在我希望在调用 B<x> + int 时,将 B<x> 转换为 A<x>
int main(){
    std::cout<<(A<12>{9}+10)<<std::endl;//OK
    std::cout<<(B<12>{9}+10)<<std::endl;//Error
    return 0;
}

我阅读了 模板类重载运算符时的隐式转换,并撰写了以下内容:

template<int x>
struct B{
    int b=3;
    operator A<x>(){
        return {b+10};
    }
    friend int operator+(A<x> a, int b);
};

我曾尝试使用 friend int operator+(A<x> a, int b) 声明来实现相关操作,但由于其与 template<int x> int operator+(A<x> a, int b) 不匹配,因此未能成功。

我阅读了 C++ - How to declare a function template friend for a class template 并添加了模板友元声明,但模板参数无法推断。

当然,我可以为 A 和 B 分别编写 operator+,但我有数十个运算符,而且不想这样做。

请问正确的解决方法是什么?


我认为,你不能使用相同的运算符,除非你在B内声明模板友元运算符,或者B是从A派生而来。 - Swift - Friday Pie
你不能制作operator+模板吗? - W.F.
@W.F. 对的,在提供的代码中它没有被声明为模板,它的模板应该在类中定义,以便与类模板实例同时存在。如果B是从它派生出来的,就可以使用为A定义的运算符。 - Swift - Friday Pie
你能触摸到 A 吗? - T.C.
@T.C. 是的,我也可以更改 A。 - eivour
4个回答

5

针对为类 A 创建非成员 operator+ 的两种方法,我们可以使用函数模板:

template <int x>
int operator+(A<x>, int);

这将不匹配B<x>,因为我们只是进行模板推导,而不允许转换。

或者,我们可以将其设置为友元非模板:

template <int x>
struct A {
    friend int operator+(A a, int );
};

这也不会匹配B<x>,因为名称查找不会考虑该函数。 除非我们告诉它:

template <int x>
struct B {
    friend int operator+(A<x>, int ); // NB: not a template
};

现在,我们考虑原始的非模板 operator+,进行所需的转换,您的代码将打印29。

@Barry 我认为他的意思是之前在两个类之外定义的二进制operator+(...)现在已经不被A所看到了。因此,你是正确的,代码打印出29,但是打印出19的第一行会抛出一个错误。从你的回答中并不能清楚地看出这一点。 - pingul
@pingul 嗯?不,只要在类中定义了A操作符,两种方式都可以工作。 - Barry
@Barry,你的意思是我应该在B<x>和A<x>中都定义(而不是声明)operator+吗? - eivour
@Barry 也许你可以提供一个可运行的例子吗?我尝试解释你的答案,但是我无法复现结果。 - pingul
@Barry 哦,我明白了;我误解了你所说的将函数设为“友元非模板”的意思。我在你的答案中添加了一个完整可用的示例。如果你不喜欢它,可以选择不接受,但这对我理解你的答案很有必要。 - pingul
显示剩余2条评论

1

你可能已经看到过这个了,但是仍然可以进行显式转换,并且可能很有用:

int main(){
    std::cout<<(A<12>{9}+10)<<std::endl;                     // prints 19
    std::cout<<(static_cast<A<12>>(B<12>{9})+10)<<std::endl; // prints 29
    return 0;
}

1

我尝试使用以下(可运行的)代码编辑Barry的答案,它产生了正确的输出,但是被拒绝了。

如果其他人也感兴趣,我会在这里添加它。

#include <iostream>

template <int x>
struct A {
    int a;
    friend int operator+(A a, int b) { return a.a + b; }
};

template <int x>
struct B {
    int b;
    operator A<x>() { return {b+10}; }
    friend int operator+(A<x>, int );
};

int main() {
    std::cout << (A<12>{9} + 10) << std::endl;
    std::cout << (B<12>{9} + 10) << std::endl;
}

Which prints

19
29

为什么当我将结构体B中的friend int operator+(A<x>, int );更改为类似friend int operator+(A<12>, int );时会出现错误?难道不应该导致相同的结果吗? - Phil

0

希望这能对你有所帮助。

我有一个模板友元 operator+

A<x> 派生并编译通过,但在调用 friend operator+ 后,变量 a 未初始化,因此我得到了立即值。你必须以某种方式设置 a,这似乎可以解决问题。

#include <iostream>

template<int x>
struct A
{
    int a{x};
};

template<int x>
int operator+(A<x> a, int b)
{
    return a.a+b;
}

template<int x>
struct B : A<x>
{
    int b;

template<int U>
    friend int operator+(A<U> a, int b);
};

int main(void)
{
    std::cout<< (A<20>{}+10)<<std::endl; // prints 30
    std::cout<< (B<10>{}+10)<<std::endl; // prints 20
    return 0;
}

它编译通过了,但是结果却改变了。我想在从B转换为A时自动添加10。同时,我也不希望用户在使用B时更改a的值。 - eivour
@eivour 它不会改变任何 A 的实例。我会看一下在转换时如何添加 10 - kocica

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