【问题标题】:c++ implicit conversion on user-defined operator for template classesc ++模板类的用户定义运算符的隐式转换
【发布时间】:2018-01-25 08:44:39
【问题描述】:

我有一个结构模板 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&lt;x&gt;,可以转换为A&lt;x&gt;

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

现在我希望在调用B&lt;x&gt; + int 时将B&lt;x&gt; 转换为A&lt;x&gt;

int main(){
    std::cout<<(A<12>{9}+10)<<std::endl;//OK
    std::cout<<(B<12>{9}+10)<<std::endl;//Error
    return 0;
}

我读了Implicit conversion when overloading operators for template classes并写了

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&lt;x&gt; a, int b)template&lt;int x&gt; int operator+(A&lt;x&gt; a, int b) 不匹配。

我看了C++ - How to declare a function template friend for a class template,做了朋友声明模板,但是因为模板参数无法推导,所以没用。

当然,我可以为 A 和 B 都写 operator+,但是我有几十个运算符,我不想这样做。

这样做的正确方法是什么?

【问题讨论】:

  • 我认为,你不能使用相同的运算符,要么在 B 中声明模板友元运算符,要么除非 B 会从 A 派生
  • 你不能制作operator+模板吗?
  • @W.F.是的,它没有在提供的代码中声明为模板,它的模板应该在类中定义为与类模板实例同时存在。如果 B 派生自它,它将能够使用为 A 定义的运算符。
  • 你能碰A吗?
  • @T.C.是的,我也可以改变 A。

标签: c++ templates operator-overloading c++14 friend-function


【解决方案1】:

查看为A 制作非成员operator+ 的两种方法,我们可以将其制作为函数模板:

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

不会匹配B&lt;x&gt;,因为我们只是在做模板推导,不允许转换。

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

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

这也不会匹配B&lt;x&gt;,因为名称查找不会考虑该功能。除非,也就是说,我们告诉它:

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

现在,将考虑我们原来的非模板operator+,将根据需要执行转换,并且您的代码打印 29。

【讨论】:

  • 但是我无法添加 A 和 int。
  • @eivour 不明白。为什么你不这么认为?
  • @Barry 我认为他的意思是A 不再看到之前在两个类之外定义的二进制operator+(...)。因此,代码打印29 是正确的,但是打印19 的第一行会引发错误。从您的回答来看,这并不清楚。
  • @pingul 嗯?不,如果您在类中定义了 A 运算符,两者都可以工作。
  • @Barry 你的意思是我应该在 B 和 A 中定义(而不是声明)operator+?
【解决方案2】:

我尝试使用以下(可运行)代码编辑 Barrys 答案,该代码会产生正确的输出,但在那里被拒绝。

我会在这里添加它以防其他人好奇。

#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;
}

打印出来的

19
29

【讨论】:

  • 为什么我换朋友 int operator+(A, int ); 时会报错在结构 B 中类似于朋友 int operator+(A, int );?不应该导致同样的结果吗?
【解决方案3】:

你可能已经看过了,但至少仍然可以进行显式转换,而且它可能很有用:

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;
}

【讨论】:

    【解决方案4】:

    希望对你有帮助。

    我已经模板化了朋友operator+

    派生自 A&lt;x&gt; 并编译,但在调用 friend operator+ 变量后,a 是 uninit,所以我得到了立即值。您必须以某种方式设置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 它不会更改A 的任何实例。我看看我们如何在转换时添加10
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-18
    • 1970-01-01
    • 2012-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多