考虑这个(而不是)简单的示例:
#include <iostream>
struct Out {
int value;
};
template<class Sink> decltype(auto) operator<<(Sink &&s, Out const &out) {
return out.value > 0? s << out.value : s;
}
struct In {
std::ostream &sink;
template<typename T> In &operator<<(T const &t) {
return sink << t, *this;
}
};
int main() {
In in{std::cout};
in << (1 << Out{3}) << '\n'; // Ok
in << Out{42} << '\n';
// error: use of overloaded operator '<<' is ambiguous
// (with operand types 'In' and 'Out')
}
这种歧义可以解决吗?我们有两个类,每个类都定义一个这样的运算符重载以将其转发到其内部类型(这些类是由两个不同的人独立设计的,而另一个人则试图在同一应用程序中使用它们。)可以按照其他运算符的形式重新定义,例如,这里没有必要放弃A
的{{1}}并尝试将friend operator<<
转换为A
;并使用某种复杂的SFINAE来排除某些重载似乎仍然无济于事。
答案 0 :(得分:2)
您可能会创建其他更合适的重载:
decltype(auto) operator<<(In& in, Out const &out) {
return in.operator<<(out);
}
decltype(auto) operator<<(In&& in, Out const &out) {
return in.operator<<(out);
}
答案 1 :(得分:0)
某些半老式SFINAE似乎可以解决问题,因为在某种意义上,它已被gcc和clang接受(并且它们均打印“ 8”和“ 42”):
#include <iostream>
#include <utility>
template<typename S, typename T> class can_shl {
using Left = char;
struct Right { char c[2]; };
template<typename U> static constexpr decltype(
std::declval<S>() << std::declval<U>(), Left{})
has_it(U &&);
static constexpr Right has_it(...);
public:
static constexpr bool value = sizeof(has_it(std::declval<T>())) == 1;
};
struct Out {
int value;
};
template<class Sink> auto operator<<(Sink &&s, Out const &out)
-> std::enable_if_t<!can_shl<Sink, Out>::value, decltype(out.value > 0? s << out.value : s)>
{
return out.value > 0? s << out.value : s;
}
struct In {
std::ostream &sink;
template<typename T> In &operator<<(T const &t) {
return sink << t, *this;
}
};
int main() {
In in{std::cout};
in << (1 << Out{3}) << '\n'; // Ok
in << Out{42} << '\n';
}
我个人对“仅在不自行编译的情况下才允许使用此方法”感到不太自信(这是一个(静态)UB吗?如果我是C ++标准,我会怎么说?)