C++友元函数模板重载和SFINAE在clang++、g++、vc++中的不同行为(C++14模式) [英] C++ friend function template overloading and SFINAE different behaviors in clang++, g++, vc++ (C++14 mode)

查看:46
本文介绍了C++友元函数模板重载和SFINAE在clang++、g++、vc++中的不同行为(C++14模式)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

So, the following code builds and runs successfully under clang++ (3.8.0), but fails both under g++ (6.3.0) and vc++ (19.10.24903.0). Both g++ and vc++ complain about redefinition of operator&&.

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

Does anyone know which compiler is at fault here. For the compilers that fails to compile the code, what would be the workarounds for the compilation error?

#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,而不是在模板的单个成员级别.awaitable 是一个有效的实例化,因此它实例化了类的所有 3 个成员的声明,复制了后两个成员.

SFINAE works at template instantiation level, i.e. at struct awaitable<T>, not at the level of individual members of the template. awaitable<void> is a valid instantiation and as such it instantiates the declarations of all 3 members of the class, duplicating the latter 2.

并不是这两个定义相互冲突——而是每个定义与自身发生冲突(示例)(更多详情).

It is not that the two definitions conflict with each other - it is that each definition conflicts with itself (example) (more details).

解决方法 1

在类外定义辅助操作符(当然,与您所拥有的不完全相同 - 这些将成为模板的任何实例的朋友)

Define the helper operators out-of-class (granted, not exactly the same as what you had - these would be friends to any instance of the template)

#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 的特例,而特化是其他一切.

Use no SFINAE at all, but a specialization of awaitable. Note the specialization is reversed - the base implementation is a special case for awaitable<void> and the specialization is for everything else.

#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模式)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆