Действительно ли это С++ 11
У меня есть следующий код, который компилируется под g++, но не с clang.
Clang будет компилировать код, если он будет изменен различными способами, например, слияние объявлений пространства имен.
// The problem disappears without namespaces.
namespace Root {
// The problem disappears if 'g' is in the global namespace, and we change
// the friend declaration to '::g'
// The problem disappears if 'g' has void return type.
// The problem disappears if we get rid of the 'Value' template argument
// and the 'value' parameter.
template<typename Value, typename Defaulted = void>
bool g(Value value);
// The problem disappears if MyClass is not a template.
template<typename ClassValue>
class MyClass {
private:
template<typename Value, typename Defaulted>
friend bool g(Value value);
};
}
// The problem disappears if we declare the Root namespace in a single block
// containing 'g', 'MyClass' and 'f'.
// The problem remains if we declare f in the global namespace and reference
// Root::g.
namespace Root {
void f() {
MyClass<int> value;
g(value);
}
}
Скомпилировать с clang:
clang -fsyntax-only -std=c++11 testcase.cpp
Скомпилировать с g++:
g++ -fsyntax-only -std=c++11 testcase.cpp
Версии: g++ 4.9.2, clang 3.6.0, как на ядре Ubuntu 15.04.
Clang дает сообщение об ошибке:
testcase.cpp:24:9: error: no matching function for call to 'g'
g(value);
^
testcase.cpp:14:21: note: candidate template ignored: couldn't infer template argument 'Defaulted'
friend bool g(Value value);
^
1 error generated.
Ответы
Ответ 1
Я считаю, что это ошибка clang. Из [temp.param] имеем:
Если определено объявление шаблона функции друга шаблон-аргумент по умолчанию, это выражение должно быть определением и должно быть единственным объявлением в шаблоне перевода.
Набор аргументов-шаблонов по умолчанию, доступных для использования, получается путем слияния аргументов по умолчанию из все предшествующие объявления шаблона аналогичны аргументам функции по умолчанию (8.3.6).
Последнее означает, что мы можем написать:
template <typename T, typename U=int>
void h();
template <typename T, typename U>
void h() { }
h<int>();
И это прекрасно сформированный код, который компилирует clang. Мы не можем указать аргумент шаблона по умолчанию для g
на основе приведенного правила, поскольку g
объявлен ранее, но не указывая его, он должен сохранить Defaulted
доступным для использования как void
через шаг слияния. Если аргумент по умолчанию доступен, тогда поиск должен иметь возможность найти g
, который мы хотим.
Обходной путь состоял бы в том, чтобы просто дружить с специализацией, о которой мы заботимся:
friend bool g<>(MyClass value);