【问题标题】:SFINAE for generic operation on generic typeSFINAE 用于泛型类型的泛型操作
【发布时间】:2017-10-16 14:34:54
【问题描述】:

我有

  • 一个类Value,可以用不同的类型(Foo, Bar, int,...)构造。

  • Value 应该有像

  • 我在类定义之外添加了运算符

我必须要以下代码:

#include <iostream>

struct Foo {
};

struct Bar {
};

struct Value {
    template<typename T>
    Value(T) {
    }
};

std::ostream &operator<<(std::ostream &os, const Bar &) {
    return os << "Bar\n";
}

std::ostream &operator<<(std::ostream &os, const Value &value) {

    auto visitor = [&](auto a) -> decltype(os << a) {
        return os << a;
    };

    // Works
    visitor(Bar{});

    // Infinity call of this function with Value.
    visitor(Foo{});

    return os;
}

int main() {
    std::cout << Value(1);

    return 0;
}

Live example.

问题:如果底层类型没有实现操作符Value的操作符no match for call operator 以将 SFINAE 与我的访问者模式一起使用(此处未显示)。

我需要的是这样的:

[&](auto a) -> std::enable_if_t<addressof?(os << a) != addressof?(os << Value{})>::value> {
    return os << a;
};

如果功能相同,则禁用此 lambda。这可能吗?

没有价值的解决方案:

  • 明确价值
  • 禁止使用 Foo 构造值

【问题讨论】:

    标签: c++ templates c++17 sfinae enable-if


    【解决方案1】:

    您可以添加一个包装器来强制只进行一次转换:

    template <typename T>
    struct OneConversion
    {
        OneConversion(const T& t) : t(t) {}
    
        operator const T&() const {return t;}
    
        const T& t;  
    };
    
    template <typename T>
    struct isOneConversion : std::false_type {};
    
    template <typename T>
    struct isOneConversion<OneConversion<T>> : std::true_type {};
    
    struct Value {
        template<typename T, std::enable_if_t<!isOneConversion<T>::value>* = nullptr>
        Value(T) {}
    };
    
    std::ostream &operator<<(std::ostream &os, const Value &value) {
    
        auto visitor = [&](auto a) -> decltype(os << OneConversion<decltype(a)>(a)) {
            return os << OneConversion<decltype(a)>(a);
        };
    
        // Works
        visitor(Bar{});
    
        visitor(Foo{}); // Error as expected.
    
        return os;
    }
    

    Demo

    【讨论】:

      【解决方案2】:

      在不修改 std::ostream &amp;operator&lt;&lt;(std::ostream &amp;os, const Value &amp;value) 的签名的情况下,我们可以检查对 operator&lt;&lt; 的调用是否符合我们的 lambda 中推导出的 a 类型:

      auto visitor = [&](auto a) -> decltype(
                                    static_cast<std::ostream&(*)(std::ostream&, const decltype(a)&)>(&operator<<)
                                   (os, a)
                                   )
      {
          return os << a;
      };
      

      适用于 Bar,失败并出现 Foo 并显示错误消息:

      error: invalid static_cast from type '<unresolved overloaded function type>' to type 'std::ostream& (*)(std::ostream&, const Foo&
      

      Demo

      【讨论】:

        【解决方案3】:

        你可以替换

        std::ostream &operator<<(std::ostream &os, const Value &value)
        {
            // ...
        }
        

        template <typename T, typename = std::enable_if_t<std::is_same_v<T, Value>>>
        std::ostream &operator<<(std::ostream &os, const T &value)
        {
            // ...
        }
        

        您仍然可以使用它打印 Value 对象,但无需隐式转换。

        将其放入您的代码中使其在visitor(Foo{}); 失败并出现以下错误,这似乎是您想要的。

        ...
        main.cpp:29:12: error: no match for call to '(operator<<(std::ostream&, const T&) [with T = Value; <template-parameter-1-2> = void; std::ostream = std::basic_ostream<char>]::<lambda(auto:1)>) (Foo)'
         visitor(Foo{});
         ~~~~~~~^~~~~~~
        ...
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-10-14
          • 1970-01-01
          • 1970-01-01
          • 2021-12-06
          • 1970-01-01
          • 2019-05-30
          • 2012-12-19
          相关资源
          最近更新 更多