C++ 友元函数模板重载和 SFINAE 在 clang++、g++、vc++(C++14 模式)中的不同行为

标签 c++ operator-overloading sfinae friend-function function-templates

因此,以下代码在 clang++ (3.8.0) 下构建并成功运行,但在 g++ (6.3.0) 和 vc++ (19.10.24903.0) 下均失败。 g++ 和 vc++ 都提示 operator&& 的重新定义。

有谁知道是哪个编译器出了问题。对于无法编译代码的编译器,编译错误的解决方法是什么?

#include <functional>
#include <iostream>

template <typename T>
struct awaitable
{
    friend awaitable<void> operator&&(awaitable a1, awaitable a2)
    {
        std::cout << "operator&&(awaitable a1, awaitable a2) - T: " << typeid(T).name() << std::endl;
        return awaitable<void>{};
    }

    template <typename U = T, typename std::enable_if<!std::is_same<U, void>::value>::type* = nullptr>
    friend awaitable<void> operator&&(awaitable<void> a1, awaitable<U> a2)
    {
        std::cout << "operator&&(awaitable<void> a1, awaitable<U> a2) - U: " << typeid(T).name() << std::endl;
        return awaitable<void>{};
    }

    template <typename U = T, typename std::enable_if<!std::is_same<U, void>::value>::type* = nullptr>
    friend awaitable<void> operator&&(awaitable<U> a1, awaitable<void> a2)
    {
        std::cout << "operator&&(awaitable<U> a1, awaitable<void> a2) - U: " << typeid(T).name() << std::endl;
        return awaitable<void>{};
    }
};

int main(int argc, const char * argv[])
{
    awaitable<int> a1, a2, a3, a4;
    auto ar = a1 && (a1 && a2) && (a2 && a3) && a4;
}

clang ++:http://coliru.stacked-crooked.com/a/cb01926bbcacdfb0

g++: http://coliru.stacked-crooked.com/a/73d17a5ae26f22eb

VC++:http://webcompiler.cloudapp.net/

最佳答案

SFINAE 在模板实例化级别工作,即在 struct awaitable<T> ,而不是在模板的单个成员级别。 awaitable<void>是一个有效的实例化,因此它实例化了类的所有 3 个成员的声明,复制了后 2 个。

这并不是说这两个定义相互冲突——而是每个定义都与自己冲突 ( example ) ( more details )。

解决方法 1

在类外定义辅助运算符(当然,与您所拥有的不完全相同 - 这些将是模板的任何实例的 friend )

#include <functional>
#include <iostream>

template <typename T>
struct awaitable
{
    friend awaitable<void> operator&&(awaitable a1, awaitable a2)
    {
        std::cout << "operator&&(awaitable a1, awaitable a2) - T: " << typeid(T).name() << std::endl;
        return {};
    }

    template<typename U>
    friend std::enable_if_t<!std::is_void<U>::value, awaitable<void>> operator&&(awaitable<void> a1, awaitable<U> a2);

    template<typename U>
    friend std::enable_if_t<!std::is_void<U>::value, awaitable<void>> operator&&(awaitable<U> a1, awaitable<void> a2);

};

template<typename U>
std::enable_if_t<!std::is_void<U>::value, awaitable<void>> operator&&(awaitable<void> a1, awaitable<U> a2)
{
    std::cout << "operator&&(awaitable<void> a1, awaitable<U> a2) - U: " << typeid(U).name() << std::endl;
    return {};
}

template<typename U>
std::enable_if_t<!std::is_void<U>::value, awaitable<void>> operator&&(awaitable<U> a1, awaitable<void> a2)
{
    std::cout << "operator&&(awaitable<U> a1, awaitable<void> a2) - U: " << typeid(U).name() << std::endl;
    return {};
}

int main(int argc, const char * argv[])
{
    awaitable<int> a1, a2, a3, a4;
    auto ar = a1 && (a1 && a2) && (a2 && a3) && a4;
}

解决方法 2

根本不使用 SFINAE,而是使用 awaitable 的特化.请注意,特化是相反的 - 基本实现是 awaitable<void> 的特例特化适用于其他一切。

#include <functional>
#include <iostream>

template <typename T, bool isvoid = std::is_void<T>::value>
struct awaitable
{
    friend awaitable<void> operator&&(awaitable a1, awaitable a2)
    {
        std::cout << "operator&&(awaitable a1, awaitable a2) - void" << std::endl;
        return {};
    }
};

template <typename T>
struct awaitable<T, false>
{
    friend awaitable<void> operator&&(awaitable a1, awaitable a2)
    {
        std::cout << "operator&&(awaitable a1, awaitable a2) - T: " << typeid(T).name() << std::endl;
        return {};
    }

    friend awaitable<void> operator&&(awaitable<void> a1, awaitable<T> a2)
    {
        std::cout << "operator&&(awaitable<void> a1, awaitable<T> a2) - U: " << typeid(T).name() << std::endl;
        return {};
    }

    friend awaitable<void> operator&&(awaitable<T> a1, awaitable<void> a2)
    {
        std::cout << "operator&&(awaitable<T> a1, awaitable<void> a2) - void" << std::endl;
        return {};
    }
};

int main(int argc, const char * argv[])
{
    awaitable<int> a1, a2, a3, a4;
    auto ar = a1 && (a1 && a2) && (a2 && a3) && a4;
}

关于C++ 友元函数模板重载和 SFINAE 在 clang++、g++、vc++(C++14 模式)中的不同行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41729107/

相关文章:

C++模板参数推导失败

c++ - SFINAE 用于泛型类型的泛型操作

javascript - 在Typescript中实现同一类的两个对象之间的加法

c++ - 需要帮助在 OpenGL 中移动相机

c++ - 通过 or-ing 其他枚举器生成的枚举器值

c++ boost进程间交换(复制)非共享和共享字符串 vector

c++ - cpp 为什么 s.query 会被覆盖

c++ - 不能重载 = 使 object1 = 0;

c++ - 我可以在控制流语句中使用 SFINAE 测试吗?

c++ - 具有显式参数和 sizeof 的 Variadic 模板... Visual Studio 2013