2

考虑这个(相当)简单的例子:

#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'sfriend operator<<并尝试转换Aint;是没有用的 并且使用某种复杂的 SFINAE 来排除一些重载看起来仍然没有帮助。

4

2 回答 2

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);
}
于 2018-07-28T07:02:24.387 回答
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++ 标准,我会怎么说?)

于 2018-07-28T14:09:38.550 回答