C++11 使用std::max会导致链接错误吗?

C++11 使用std::max会导致链接错误吗?,c++11,C++11,考虑在头文件中定义的库 struct Proj { struct Depth { static constexpr unsigned Width = 10u; static constexpr unsigned Height = 10u; }; struct Video { static constexpr unsigned Width = 10u; static constexpr unsigned Hei

考虑在头文件中定义的库

struct Proj {
    struct Depth {
        static constexpr unsigned Width = 10u;
        static constexpr unsigned Height = 10u;
    };
    struct Video {
        static constexpr unsigned Width = 10u;
        static constexpr unsigned Height = 10u;
    };
};
这个库被编译,我现在正在开发一个应用程序来链接这个库。我一直认为这是某种可见性问题,但即使在添加了CMake everywhere的B_EXPORT standard可见性内容(没有任何更改)之后,我最终还是发现了问题

template <class Projection>
struct SomeApplication {
    SomeApplication() {
        unsigned height = std::max(// or std::max<unsigned>
            Projection::Depth::Height,
            Projection::Video::Height
         );
    }
};
一切顺利。也就是说,仅仅使用Projection::XXX似乎并没有任何关于可见性的具体问题

你有没有想过这可能是什么原因?这是在OSX上,所以。

问题是宽度和高度是声明的,而不是在结构中定义的。实际上,这意味着没有为它们分配存储

现在回想一下std::max的签名:

注意引用:这意味着要获取参数的地址。但是,由于宽度和高度只是声明的,所以它们没有任何存储空间!因此出现了链接器错误

现在让我们考虑你剩下的问题。< /P> 您手工编写的max可以工作,因为您从不使用任何指向变量的指针或引用

您可能无法在一个玩具示例上重现这一点,因为根据具体的优化级别,一个足够智能的编译器可能会在编译时计算max,从而省去在运行时获取地址的麻烦。例如,将disasm与gcc 7.2进行比较:评估确实是在编译时完成的

至于解决办法,这要看情况而定。您有几个选项,仅举几个:

使用constexpr getter函数而不是变量。在这种情况下,它们返回的值的行为更像是临时对象,允许获取地址,编译器肯定会对此进行优化。这是我通常建议的方法。 使用名称空间而不是结构。在这种情况下,除了声明变量外,还将定义变量。需要注意的是,如果在多个翻译单元中使用符号错误,则可能会出现重复的符号错误。对此的修复仅以C++17内联变量的形式进行。 …说到这里,C++17还更改了constexpr静态成员变量的规则,默认情况下这些变量是内联的,因此,如果您切换到此标准,就不会出现此错误。
哇!有了这样一个清晰、深思熟虑的答案,这是非常有意义的——感谢您如此仔细地解释!!!我将不得不使用1,我的项目的依赖关系需要C++11,并且这个Proj类被用作许多其他可能的预测之一。保持它们的自包含性允许结构定义框架的其余部分。我期待着继续学习C++17,这也是这样做的另一个原因
unsigned height = Projection::Depth::Height;
if (height < Projection::Video::Height)
    height = Projection::Video::Height;
template<typename T>
const T& max(const T&, const T&);