Подтвердить что ты не робот

Действительно ли это С++ 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.
4b9b3361

Ответ 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);