3
votes

Here's about the most minimal example I can produce that displays this behaviour:

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>>;

This is used as follows:

main.cpp

#include "bar.h"

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

Compiling this as under GCC (I've tried both 7.4.0 and 8.3.0):

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

Gives the error:

bar.cpp:(.text._ZN3barI3fooILi0EEEC2Ei[_ZN3barI3fooILi0EEEC5Ei]+0x3c): undefined reference to `foo<0>::foo(int)'

Clang versions 4 to 7 seem to accept this.

Two small changes make GCC accept it:

  1. Removing the constexpr operator"" definition, or
  2. Changing both the operator"" and the foo constructor to inline instead of constexpr.

Is this legal, and does GCC have a valid reason for rejecting this as-is?

1
Do you also get the problem if compiling all the .cpp files (without going via shared library) - M.M
@M.M Yes, same issue. I'll update the question. - Yuushi
Just a note, it works with -std=c++17. - Meowmere

1 Answers

1
votes

I tried a lot with your sources and got following results:

gcc9.1.0 compiles without any problem!

And now the curious with gcc 8.3.0:

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

fails as you mentioned!

but without -Wall it compiles!

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

and also

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

I have no idea why the flag -Wall has anything todo with the generated intermediate files. -Wallshould never effect any generated code as it is only a diagnostic thing. So for me it is quite simply a gcc bug! So please fill a bug report!