Проверка того, существует ли функция, не являющаяся членом, которая принимает T param

Я хочу проверить, существует ли функция, не являющаяся членом, которая принимает тип параметра Т. Для этого я использовал void_t "трюк", представленный г-ном Вальтером Э. Брауном в cppcon (тот же трюк работает без каких-либо проблем, чтобы проверить, существует ли тип члена или функция-член).

#include <iostream>
#include <type_traits>

template<typename...>
using void_t = void;

void Serialize(float&)
{
}

template<typename T, typename = void>
struct has_external_serialize : std::false_type
{
};

template<typename T>
struct has_external_serialize<T, void_t<decltype(Serialize(std::declval<T&>()))>> : std::true_type
{
};

void Serialize(int&)
{
}

int main(int argc, const char * argv[])
{
    std::cout<<has_external_serialize<float>::value<<has_external_serialize<int>::value;
}

Этот код печатает 11 при компиляции с использованием GCC и 10 при компиляции с помощью clang (xcode 5.1.1).

Мои вопросы - этот код правильный? Если да, есть ли ошибка в clang или ошибка в GCC или код находится в определенной области, определенной для реализации, и я не могу предположить, что он будет иметь такое же поведение на всех платформах?

Ответы

Ответ 1

Расхождение между компиляторами обусловлено определением void_t: Есть ли ошибка компилятора, представленная моей реализацией свойства типа is_complete? Короче говоря, в стандарте неясно, могут ли неиспользуемые аргументы в специализированных шаблонах псевдонимов привести к смене замены или просто игнорируются. Резолюция CWG issue 1558 поясняет, что более короткое определение void_t в вопросе должно работать.

С этой проблемой работала с использованием

template<typename... Ts>
struct make_void { typedef void type;};

template<typename... Ts>
using void_t = typename make_void<Ts...>::type;

оба компилятора производят 10.

§14.6.4.2 [temp.dep.candidate]:

Для вызова функции, который зависит от параметра шаблона, функции-кандидата найдены с использованием обычных правил поиска (3.4.1, 3.4.2, 3.4.3), за исключением того, что:

  • Для части поиска, использующей поиск неквалифицированного имени (3.4.1) или поиск по квалифицированному имени (3.4.3), только объявления функций из контекст определения шаблона.
  • Для части поиска с использованием связанных пространств имен (3.4.2) только объявления функций, найденные в контексте определения шаблона или контекст экземпляра шаблона.

Если имя функции является неквалифицированным идентификатором, и вызов будет плохо сформировался или нашел бы лучшее совпадение, если бы поиск в пределах связанные пространства имен считаются всеми объявлениями функций с внешняя связь, введенная в эти пространства имен во всем переводе единиц, а не только с учетом тех объявлений, которые указаны в шаблоне контекста определения контекста и шаблона, то программа undefined.

Неквалифицированный поиск Serialize выполняется в контексте определения шаблона и не находит Serialize(int &), и нет ADL для аргумента типа int&, поэтому 10 является правильным выходом.