3

Итак, следующий код строит и запускается под управлением clang ++ (3.8.0), но не работает как в g ++ (6.3. 0) и vC++ (19.10.24903.0). И g ++, и vC++ жалуются на переопределение оператора & &.Перегрузка шаблонов функций в C++ и SFINAE разные поведения в clang ++, g ++, vC++ (режим C++ 14)

Кто-нибудь знает, какой компилятор здесь виноват. Для компиляторов, которые не могут скомпилировать код, какими будут обходные пути для ошибки компиляции?

#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; 
} 

лязг ++: http://coliru.stacked-crooked.com/a/cb01926bbcacdfb0

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

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

ответ

3

SFINAE работает на уровне экземпляра шаблона, то есть в struct awaitable<T>, а не на уровне отдельных членов шаблона. awaitable<void> является действительным экземпляром, и поэтому он создает экземпляры всех трех членов класса, дублируя их. 2.

Это не значит, что оба определения конфликтуют друг с другом - каждый конфликт конфликтует с самим собой (example) (more details).

Обход 1

Определить вспомогательные операторы вне класса (предоставившей, не точно такие же, как то, что у вас - это было бы друзей к любой экземпляра шаблона)

#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; 
} 
+0

Похоже, что это связано с ошибками компиляции как для g ++, так и для vC++! Но поведение мудрое, ли clang ++ ведет себя правильно или g ++/vC++? – Dejavu

+0

Добавлено несколько подробностей. – rustyx

+0

Значит, вы имеете в виду, что clang является неисправным здесь, не предотвращая противоречивые определения? Или, clang смог каким-то образом «слить» любые противоречивые определения, если они «точно» одинаковы? – Dejavu

 Смежные вопросы

  • Нет связанных вопросов^_^