首页 > 解决方案 > 对外部模板 constexpr 构造函数的未定义引用

问题描述

这是我可以生成的显示此行为的最小示例:

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

酒吧.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>>;

酒吧.cpp

#include "bar.h"

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

template struct bar<foo<0>>;

用法如下:

主文件

#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 是否有正当理由拒绝这样做?

标签: c++templatesconstexpr

解决方案


我对您的资源进行了很多尝试,并得到了以下结果:

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 错误!所以请填写错误报告!


推荐阅读