Функции друга Constexpr в MSVC - PullRequest
0 голосов
/ 23 февраля 2019

Я пытаюсь определить друга-оператора constexpr в шаблоне.Я получил ошибку компилятора при попытке создать экземпляр этого оператора в контексте non-constexpr.Если я определю тот же оператор в качестве члена класса шаблона в качестве бесплатной функции шаблона, он будет работать нормально.

template <typename T>
struct A
{
    T Value;

    // error
    friend constexpr bool operator== (const A& l, const A& r)
    {
        return l.Value == r.Value;
    }

    // ok
    constexpr bool operator!= (const A& r) const
    {
        return Value != r.Value;
    }
};

// ok
template <typename T> constexpr bool
operator< (const A<T>& l, const A<T>& r)
{
    return l.Value < r.Value;
}

#include <string>
int main ()
{
    A<std::string> s;
    bool ret = (s < s, s == s, s != s);
}

У меня ошибка:

<source>(7): error C3615: constexpr function 'operator ==' cannot result in a constant expression

<source>(9): note: failure was caused by call of undefined function or one not declared 'constexpr'

<source>(9): note: see usage of 'std::operator =='

<source>(8): note: while compiling class template member function 'bool operator ==(const A<std::string> &,const A<std::string> &)'

<source>(29): note: see reference to class template instantiation 'A<std::string>' being compiled

Godbolt link

Является ли эта дискриминация «другом» требованием стандарта или ошибки компилятора?

Ответы [ 2 ]

0 голосов
/ 25 февраля 2019

[dcl.constexpr] говорит следующее:

Если конкретизированная специализация шаблона шаблона функции constexpr или функции-члена шаблона класса не удовлетворяет требованиям для функции constexpr или конструктора constexprэта специализация по-прежнему является функцией constexpr или конструктором constexpr, даже если вызов такой функции не может появляться в константном выражении.

Так что создание шаблона вашего класса с помощью std::string вполне нормально,и ваши функции сравнения по-прежнему constexpr, хотя вызовы к ним не константные выражения (проверьте это, объявив ret constexpr).Маркировка этих функций constexpr вообще ничего не купит (в данном случае), но это совершенно законно.

Далее стандарт говорит:

Если нет специализациишаблона будет удовлетворять требованиям для функции constexpr или конструктора constexpr, если рассматривается как не шаблонная функция или конструктор, шаблон неправильно сформирован, диагностика не требуется.

Это не похожеоднако может быть применимо к вашему случаю, так как экземпляры, например, со встроенными типами, удовлетворяют требованиям constexpr.

Ошибка компиляции MSVC не представляется оправданной.Похоже, ошибка в компиляторе.

0 голосов
/ 25 февраля 2019

Я думаю, сообщение об ошибке, которое вы получили, может вводить в заблуждение или, по крайней мере, сбивать с толку.Проблема с вашим кодом заключается в неправильном объявлении friend.

Объявление оператора внутри шаблонной структуры как friend делает его свободной функцией, так же как operator< в вашем примере, следовательно,два параметра вместо одного параметра, как в случае operator!=, как вы объявили в своем примере.Если бы вы объявили operator< другом struct A, правильный способ сделать это будет:

template <typename  X>
friend constexpr bool operator< (const A<X>& l, const A<X>& r);

То же самое относится и к operator==.Правильным объявлением будет:

template <typename X>
friend constexpr bool operator== (const A<X>& l, const A<X>& r)
{
    return l.Value == r.Value;
}

т.е. WITH template <typename X>, которое вы пропустили в своем проблемном примере и, следовательно, не скомпилировали.Ваше оригинальное объявление operator== не приведет к правильному оператору функции без друзей для struct A.

Полный список кода, включая исправление, будет следующим:

template <typename T>
struct A
{
    T Value;

    // no error anymore
    template <typename X>
    friend constexpr bool operator== (const A<X>& l, const A<X>& r)
    {
        return l.Value == r.Value;
    }

    // ok
    constexpr bool operator!= (const A& r) const
    {
        return Value != r.Value;
    }
};

Вы также можете объявить это следующим образом

template <typename T>
friend constexpr bool operator== (const A<T>& l, const A<T>& r)

с T вместо X, но на самом деле это то же самое, поскольку внутренний T переопределяет внешний T.

...