fe263
fe263

Reputation: 215

why overload operator&& error?

I try write a Expression templates.

   template<typename Tag>
struct Expr{
    tuple<Tag> value;
};
struct logic_and{};

template<typename T>
struct isExpr{enum{value=0};};
template<typename Tag>
struct isExpr<Expr<Tag>>{enum{value=1};};

template<typename L,typename R,typename=std::enable_if<isExpr<L>::value||isExpr<R>::value> >
Expr<logic_and> operator&&(const L&,const R&)
{
   return {};
}

but mingw gcc 4.8.1 error:

...include\c++\ext\string_conversions.h||In instantiation of '_Ret __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = int; _CharT = char; _Base = {int}; std::size_t = long long unsigned int]':|
...include\c++\bits\basic_string.h|2825|required from here|
...include\c++\ext\string_conversions.h|67|error: no match for 'operator||' (operand types are 'bool' and 'Expr<logic_and>')|
...include\c++\ext\string_conversions.h|67|note: candidate is:|
...include\c++\ext\string_conversions.h|67|note: operator||(bool, bool) <built-in>|
...include\c++\ext\string_conversions.h|67|note:   no known conversion for argument 2 from 'Expr<logic_and>' to 'bool'|
...include\c++\ext\string_conversions.h||In instantiation of '_Ret __gnu_cxx::__stoa(_TRet (*)(const _CharT*, _CharT**, _Base ...), const char*, const _CharT*, std::size_t*, _Base ...) [with _TRet = long int; _Ret = long int; _CharT = char; _Base = {int}; std::size_t = long long unsigned int]':|

looks find a bool result...If I replace to

template<typename L,typename R,typename=std::enable_if<isExpr<L>::value||isExpr<R>::value> >
Expr<logic_and> operator||(const L&,const R&)
{
   return {};
}

or replace to

template<typename L,typename R,typename=std::enable_if<isExpr<L>::value||isExpr<R>::value> >
bool operator&&(const L&,const R&)
{
   return {};
}

all pass! is gcc bug? how can I fix it?

edit: sorry my Misleading to enable_if....now I reproduce the bug:

#include <type_traits>
#include <memory>
namespace TestFoo{
struct Tag {};
struct shift_left:Tag {};
struct shift_right:Tag {};
struct multiplies:Tag {};
struct divides:Tag {};
struct modulus:Tag {};
struct plus:Tag {};
struct minus:Tag {};
struct less:Tag {};
struct greater:Tag {};
struct less_equal:Tag {};
struct greater_equal:Tag {};
struct equal_to:Tag {};
struct not_equal_to:Tag {};
struct logical_or:Tag {};
struct logical_and:Tag {};
struct bitwise_and:Tag {};
struct bitwise_or:Tag {};
struct bitwise_xor:Tag {};
struct shift_left_assign:Tag {};
struct shift_right_assign:Tag {};
struct multiplies_assign:Tag {};
struct divides_assign:Tag {};
struct modulus_assign:Tag {};
struct plus_assign:Tag {};
struct minus_assign:Tag {};
struct bitwise_and_assign:Tag {};
struct bitwise_or_assign:Tag {};
struct bitwise_xor_assign:Tag {};


template<typename Tag,typename...ARGS>
class Expr
{

public:
    template<typename...SRC>
    Expr(SRC const&...src){

    }
private:
};
template<typename T>
struct is_expr{
    enum{value=0};
};
template<typename Tag,typename...ARGS>
struct is_expr<Expr<Tag,ARGS...>>{
    enum{value=sizeof...(ARGS)};
};

#define BinaryOp(op,name)   \
template<typename L,typename R,typename=typename std::enable_if<is_expr<L>::value||is_expr<R>::value>::type>\
Expr<name,L,R> operator op(const L&l,const R&r)\
{\
   return  {std::forward<const L&>(l),std::forward<const R&>(r)};\
}

BinaryOp(<<,shift_left)
BinaryOp(>>,shift_right)
BinaryOp(*,multiplies)
BinaryOp(%,modulus)
BinaryOp(+,plus)
BinaryOp(-,minus)
BinaryOp(<,less)
BinaryOp(>,greater)
BinaryOp(<=,less_equal)
BinaryOp(>=,greater_equal)
BinaryOp(==,equal_to)
BinaryOp(!=,not_equal_to)
BinaryOp(||,logical_or)
BinaryOp(&&,logical_and)

}

.cpp :

#include "test.h"

using namespace TestFoo;//using namespace Appear bug
void test(){  
}

if I delete BinaryOp(&&,logical_and) ,or not using namespace in .cpp,or delete all std::forward ,Compile successfully. Otherwise eat memory Until failure.

Upvotes: 0

Views: 196

Answers (2)

Jarod42
Jarod42

Reputation: 218098

With

template<typename L, typename R, typename = typename std::enable_if<is_expr<L>::value &&    is_expr<R>::value>::type>
Expr<name, L, R> operator op(const L&l, const R&r);

You go in (compiler) recursive operator ||: this operator may evaluate the (unnamed) enum is_expr<L>::value with the enum is_expr<R>::value which need to take into account is_expr<decltype(is_expr<L>::value)>::value...

A solution is to replace the enum by a static constexpr std::size_t. (see https://ideone.com/pqhmv1)

Upvotes: 0

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275800

std::enable_if<isExpr<L>::value||isExpr<R>::value>

the above is a valid type regardless of what is inside the <> of enable_if.

typename std::enable_if<isExpr<L>::value||isExpr<R>::value>::type

is a valid type if and only if the bool expression in the <> of enable_if is true.

In short, you are not doing SFINAE successfully, so things behaving strangely is expected. In particular, your overloaded operator claims to handle const bool&||const bool& and many other pairs of types it has no business messing around with.

I cannot decode the error, but first fix the code.

Upvotes: 1

Related Questions