Reputation: 215
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
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
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