g++ 不 link 取决于优化设置
g++ does not link depending on optimization settings
所以,这让我在过去的两天里吃不消:
我不能 link 我的应用程序,这取决于我使用的编译器和使用的优化级别:
gcc
与 -O1
、-O2
、-O3
一起玩得很好,但与 -O0
和 一起玩就失败了
clang
与 -O2
和 -O3
一起玩得很好,但与 -O1
和 -O0
一起玩就失败了。
里面有一堆可怕的模板,但我看不出有什么理由会出现这种晦涩的行为。
这里是重现问题的最少量代码:
#include <map>
#include <memory>
#include <iostream>
using id_type = long;
class CB : public std::enable_shared_from_this<CB>
{
public:
CB() = default;
virtual ~CB() = default;
};
template<class F, class C> class SC : public CB
{
};
class FB
{
public:
virtual ~FB() = default;
template<class T, class B> T* as(B* v) const { return dynamic_cast<T*>(v);}
};
template<class T>
class F : public FB
{
public:
virtual std::shared_ptr<CB> create() const
{
auto n = std::make_shared<T>();
return n;
}
};
struct B
{
virtual ~B() = default;
static const id_type ID = 1;
};
class A : virtual public B, virtual public SC<A, B>
{
public:
A() = default;
};
static std::map<id_type, std::shared_ptr<FB>> crtrs {
{A::ID, std::make_shared<F<A>>()}
};
int main()
{
std::cout << crtrs.size();
}
这里和网上一样https://gcc.godbolt.org/z/sb9b5E
错误信息如下:
fld@flap ~/work/p/test1
> $ g++ -O1 main.cpp
fld@flap ~/work/p/test1
> $ g++ -O2 main.cpp
fld@flap ~/work/p/test1
> $ g++ -O3 main.cpp
fld@flap ~/work/p/test1
> $ g++ -O4 main.cpp
fld@flap ~/work/p/test1
> $ g++ -O0 main.cpp
/tmp/cc8D7sNK.o: In function `__static_initialization_and_destruction_0(int, int)':
main.cpp:(.text+0x1c0): undefined reference to `B::ID'
collect2: error: ld returned 1 exit status
fld@flap ~/work/p/test1
> $ clang++ -O0 main.cpp
/tmp/main-c49b32.o: In function `__cxx_global_var_init.1':
main.cpp:(.text.startup+0x7a): undefined reference to `B::ID'
clang-8: error: linker command failed with exit code 1 (use -v to see invocation)
fld@flap ~/work/p/test1
> $ clang++ -O1 main.cpp
/tmp/main-cf18ee.o: In function `__cxx_global_var_init.1':
main.cpp:(.text.startup+0x3c): undefined reference to `B::ID'
clang-8: error: linker command failed with exit code 1 (use -v to see invocation)
fld@flap ~/work/p/test1
> $ clang++ -O2 main.cpp
fld@flap ~/work/p/test1
> $ clang++ -O3 main.cpp
如果有人知道可能是什么原因,欢迎提供任何提示。
您没有在任何地方提供 B::ID 的定义。碰巧的是,通过更高的优化,编译器碰巧忽略了所有访问。
您需要在顶级范围内添加静态成员的定义:
const id_type B::ID;
如果只读取静态常量数据成员,则不需要单独定义,因为编译时常量不被视为 ODR 使用 (One Definition Rule)。
您完全需要定义的原因是映射构造函数期望初始化列表中的 std::map::value_type
,即 std::pair<const Key, T>
。在这种情况下选择的构造函数是 pair( const T1& x, const T2& y );
为了调用此构造函数,A::ID
的地址即 B::ID
被获取,这构成了 ODR-use,即使对于常量也是如此。
因为在这种情况下 pair 构造函数几乎是微不足道的,它在更高的优化中被内联并且对 &B::ID 的唯一引用消失了,因为 B::ID 的值是已知的并且可以直接已初始化。
另请参阅:static
如果您使用的是 C++17 或更新版本,您也可以使用 B:ID
constexpr
代替 const
,那么您不需要单独定义,因为 constexpr
隐含地 inline
(inline static const
也应该没问题)。
所以,这让我在过去的两天里吃不消:
我不能 link 我的应用程序,这取决于我使用的编译器和使用的优化级别:
gcc
与-O1
、-O2
、-O3
一起玩得很好,但与-O0
和 一起玩就失败了
clang
与-O2
和-O3
一起玩得很好,但与-O1
和-O0
一起玩就失败了。
里面有一堆可怕的模板,但我看不出有什么理由会出现这种晦涩的行为。
这里是重现问题的最少量代码:
#include <map>
#include <memory>
#include <iostream>
using id_type = long;
class CB : public std::enable_shared_from_this<CB>
{
public:
CB() = default;
virtual ~CB() = default;
};
template<class F, class C> class SC : public CB
{
};
class FB
{
public:
virtual ~FB() = default;
template<class T, class B> T* as(B* v) const { return dynamic_cast<T*>(v);}
};
template<class T>
class F : public FB
{
public:
virtual std::shared_ptr<CB> create() const
{
auto n = std::make_shared<T>();
return n;
}
};
struct B
{
virtual ~B() = default;
static const id_type ID = 1;
};
class A : virtual public B, virtual public SC<A, B>
{
public:
A() = default;
};
static std::map<id_type, std::shared_ptr<FB>> crtrs {
{A::ID, std::make_shared<F<A>>()}
};
int main()
{
std::cout << crtrs.size();
}
这里和网上一样https://gcc.godbolt.org/z/sb9b5E
错误信息如下:
fld@flap ~/work/p/test1
> $ g++ -O1 main.cpp
fld@flap ~/work/p/test1
> $ g++ -O2 main.cpp
fld@flap ~/work/p/test1
> $ g++ -O3 main.cpp
fld@flap ~/work/p/test1
> $ g++ -O4 main.cpp
fld@flap ~/work/p/test1
> $ g++ -O0 main.cpp
/tmp/cc8D7sNK.o: In function `__static_initialization_and_destruction_0(int, int)':
main.cpp:(.text+0x1c0): undefined reference to `B::ID'
collect2: error: ld returned 1 exit status
fld@flap ~/work/p/test1
> $ clang++ -O0 main.cpp
/tmp/main-c49b32.o: In function `__cxx_global_var_init.1':
main.cpp:(.text.startup+0x7a): undefined reference to `B::ID'
clang-8: error: linker command failed with exit code 1 (use -v to see invocation)
fld@flap ~/work/p/test1
> $ clang++ -O1 main.cpp
/tmp/main-cf18ee.o: In function `__cxx_global_var_init.1':
main.cpp:(.text.startup+0x3c): undefined reference to `B::ID'
clang-8: error: linker command failed with exit code 1 (use -v to see invocation)
fld@flap ~/work/p/test1
> $ clang++ -O2 main.cpp
fld@flap ~/work/p/test1
> $ clang++ -O3 main.cpp
如果有人知道可能是什么原因,欢迎提供任何提示。
您没有在任何地方提供 B::ID 的定义。碰巧的是,通过更高的优化,编译器碰巧忽略了所有访问。
您需要在顶级范围内添加静态成员的定义:
const id_type B::ID;
如果只读取静态常量数据成员,则不需要单独定义,因为编译时常量不被视为 ODR 使用 (One Definition Rule)。
您完全需要定义的原因是映射构造函数期望初始化列表中的 std::map::value_type
,即 std::pair<const Key, T>
。在这种情况下选择的构造函数是 pair( const T1& x, const T2& y );
为了调用此构造函数,A::ID
的地址即 B::ID
被获取,这构成了 ODR-use,即使对于常量也是如此。
因为在这种情况下 pair 构造函数几乎是微不足道的,它在更高的优化中被内联并且对 &B::ID 的唯一引用消失了,因为 B::ID 的值是已知的并且可以直接已初始化。
另请参阅:static
如果您使用的是 C++17 或更新版本,您也可以使用 B:ID
constexpr
代替 const
,那么您不需要单独定义,因为 constexpr
隐含地 inline
(inline static const
也应该没问题)。