指南做constexpr操作符重载? [英] Guidelines to do constexpr operator-overloading?

查看:169
本文介绍了指南做constexpr操作符重载?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑一个简单的 Wrapper 带有重载乘法的类 operator * = 。对于旧式运算符重载,可以按运算符* = 定义运算符* 甚至是 Boost.Operators 等图书馆及其通过@DanielFrey的 df.operators 来减少你的样板。



但是,对于使用新的C ++ 11 constexpr 的编译时计算,这种方便就消失了。由于后者修改其(隐式)左参数,因此 constexpr operator * 不能调用 operator * = 。此外,还有在constexpr 上没有重载 ,因此添加了一个额外的 constexpr运算符* 到现有的运算符* 会导致重载解析模糊。



我目前的方法是:

  #include< iostream> 

struct Wrap
{
int value;

Wrap& operator * =(Wrap const& rhs)
{value * = rhs.value; return * this; }

//需要注释此函数,因为使用constexpr版本重载模糊性
// friend Wrap运算符*(Wrap const& lhs,Wrap const& rhs)
/ / {return Wrap {lhs} * = rhs; }

friend constexpr Wrap运算符*(Wrap const& lhs,Wrap const& rhs)
{return {lhs.value * rhs.value}; }
};

constexpr Wrap factorial(int n)
{
return n? factorial(n-1)* Wrap {n}:Wrap {1};
}

//希望能够静态初始化这些数组
struct Hold
{
static constexpr Wrap Int [] = {factorial ),factorial(1),factorial(2),factorial(3)};
};

int main()
{
std :: cout< Hold :: Int [3] .value<< \\\
; // 6
auto w = Wrap {2};
w * = Wrap {3};
std :: cout<< w.value<< \\\
; // 6
}

此处输出。我的问题是:




  • 运算符* = 运算符* ,而不是运算符* / code>

  • 因此,Boost.Operators不再适用于减少编写许多其他算术运算符的样板。



问题:这是推荐的C ++ 11方式,同时具有运行时 operator * = 混合运行时/编译时 constexpr operator * ? C ++ 14改变这里的任何东西。减少逻辑重复?



UPDATE :@AndyProwl的答案被接受为惯用的,但根据@DyP的建议,在C + 11一个 可以减少逻辑重复,但需要额外的分配和反直觉风格。

  // define operator * = in operator of operator * 
Wrap& operator * =(Wrap const& rhs)
{* this = * this * rhs; return * this; }


解决方案我找不到一个惯用的解决方案C + +11(虽然作为解决方法, DyP的建议似乎可以接受我)。



在C ++ 14中, constexpr 并不意味着 const (见C ++ 14标准草案n3690的附录C.3.1)可以简单地定义 operator * = operator * constexpr

  struct Wrap 
{
int value;

constexpr Wrap& operator * =(Wrap const& rhs)
{value * = rhs.value; return * this; }

friend constexpr Wrap operator *(Wrap const& lhs,Wrap const& rhs)
{return Wrap(lhs)* = rhs; }
};

这是一个 生活示例 ,其中上述程序使用 -std = c ++ 1y on Clang - 不幸的是,GCC似乎没有实施这条规则。


Consider a simple int Wrapper class with overloaded multiplication operator*= and operator*. For "old-style" operator-overloading, one can define operator* in terms of operator*=, and there are even libraries like Boost.Operators and its modern incarnation df.operators by @DanielFrey that reduce the boilerplate for you.

However, for compile-time computations using the new C++11 constexpr, this convenience disappears. A constexpr operator* cannot call operator*= because the latter modifies its (implicit) left argument. Furthermore, there is no overloading on constexpr, so adding an extra constexpr operator* to the existing operator* results in an overload resolution ambiguity.

My current approach is:

#include <iostream>

struct Wrap
{
    int value;    

    Wrap& operator*=(Wrap const& rhs) 
    { value *= rhs.value; return *this; }

    // need to comment this function because of overloading ambiguity with the constexpr version
    // friend Wrap operator*(Wrap const& lhs, Wrap const& rhs)
    // { return Wrap { lhs } *= rhs; }    

    friend constexpr Wrap operator*(Wrap const& lhs, Wrap const& rhs)
    { return { lhs.value * rhs.value }; }
};

constexpr Wrap factorial(int n)
{
    return n? factorial(n - 1) * Wrap { n } : Wrap { 1 };    
}

// want to be able to statically initialize these arrays
struct Hold
{
    static constexpr Wrap Int[] = { factorial(0), factorial(1), factorial(2), factorial(3) };
};

int main() 
{
    std::cout << Hold::Int[3].value << "\n"; // 6
    auto w = Wrap { 2 };
    w *= Wrap { 3 };
    std::cout << w.value << "\n"; // 6
}

Live output here. My problems with this are:

  • duplication of the multiplication logic in both operator*= and operator*, instead of operator* being expressed in terms of operator*=
  • hence, Boost.Operators no longer works to reduce the boilerplate for writing many other arithmetic operators

Question: is this the recommended C++11 way of having both a run-time operator*= and mixed run-time/compile-time constexpr operator*? Does C++14 change anything here to e.g. reduce the logic duplication?

UPDATE: The answer by @AndyProwl is accepted as idiomatic but as per suggestion of @DyP, in C++11 one could reduce the logic duplication at the expense of an extra assignment and counter-intuitive style

    // define operator*= in terms of operator*
    Wrap& operator*=(Wrap const& rhs) 
    { *this = *this * rhs; return *this; }

解决方案

I could not find an idiomatic solution for C++11 (although as a workaround, DyP's suggestion seems acceptable to me).

In C++14 however, where constexpr does not imply const (see Annex C.3.1 of the C++14 Standard Draft n3690), you could simply define both operator *= and operator * as constexpr, and define the latter in terms of the former, as usual:

struct Wrap
{
    int value;    

    constexpr Wrap& operator *= (Wrap const& rhs) 
    { value *= rhs.value; return *this; }

    friend constexpr Wrap operator * (Wrap const& lhs, Wrap const& rhs)
    { return Wrap(lhs) *= rhs; }    
};

Here is a live example, where the above program is being compiled with -std=c++1y on Clang - unfortunately, GCC does not seem to implement this rule yet.

这篇关于指南做constexpr操作符重载?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆