Неопределенная ссылка на внешний шаблон конструктора constexpr - PullRequest
3 голосов
/ 10 июля 2019

Вот самый минимальный пример, который я могу привести, который отображает это поведение:

foo.h

#pragma once

#include <string>

template <int I>
struct foo
{
    constexpr explicit foo(int k)
        : j(k + I)
    { }

    std::string to_string() const;

private:

    int j;
};

extern template struct foo<0>;

constexpr foo<0> operator"" _foo0(unsigned long long int v)
{
    return foo<0>(static_cast<int>(v));
}

foo.cpp

#include "foo.h"

template <int I>
std::string foo<I>::to_string() const
{
    return std::to_string(j);
}

template struct foo<0>;

bar.h

#pragma once

#include "foo.h"
#include <vector>

template <typename T>
struct bar
{
    explicit bar(int i);

private:

    std::vector<T> vec;
};

extern template struct bar<foo<0>>;

bar.cpp

#include "bar.h"

template <typename T>
bar<T>::bar(int i)
{
    vec.push_back(T{i});
}

template struct bar<foo<0>>;

Используется следующим образом:

main.cpp

#include "bar.h"

int main()
{
    bar<foo<0>> b2(5);
}

Компиляция как в GCC (я пробовал 7.4.0 и 8.3.0):

g++-8 foo.cpp bar.cpp main.cpp -std=c++14 -Wall -Werror -o test

Выдает ошибку:

bar.cpp :(. Text._ZN3barI3fooILi0EEEC2Ei [_ZN3barI3fooILi0EEEC5Ei] + 0x3c): неопределенная ссылка на `foo <0> :: foo (int) '

Clang версии от 4 до 7, кажется, принимают это.

Два небольших изменения заставляют GCC принять это:

  1. Удаление определения constexpr operator"" или
  2. Изменение конструкторов operator"" и foo на inline вместо constexpr.

Является ли это законным, и есть ли у GCC веская причина для отклонения этого как-is

1 Ответ

1 голос
/ 10 июля 2019

Я много пробовал с вашими источниками и получил следующие результаты:

gcc9.1.0 компилируется без проблем!

А теперь самое интересное с gcc 8.3.0:

g++ main.cpp foo.cpp bar.cpp -std=c++14 -Wall // fails: undef reference

терпит неудачу, как вы упомянули!

, но без -Wall он компилируется!

g++ main.cpp foo.cpp bar.cpp -std=c++14  // compiles, no linker error!

, а также

g++ main.cpp foo.cpp bar.cpp -std=c++17 -Wall // compiles, no linker error!

Я понятия не имею, почему флаг -Wall имеет что-либо связанное с сгенерированными промежуточными файлами.-Wall никогда не должен влиять на сгенерированный код, так как это всего лишь диагностическая вещь.Так что для меня это просто ошибка gcc!Поэтому, пожалуйста, заполните сообщение об ошибке!

...