通过typedef强制模板实例化:在g ++上成功,在Visual C ++上失败

jav*_*ver 11 c++ g++ visual-c++ c++17 template-instantiation

我想强制模板实例化。
以下代码1在g ++(http://coliru.stacked-crooked.com/a/33986d0e0d320ad4)上可以正常工作(打印)。
但是,它0在Visual C ++(https://rextester.com/WGQG68063)上打印错误的结果()。

#include <iostream>
#include <string>
template <int& T>struct NonTypeParameter { };

//internal implementation
int lala=0;
template <typename T> struct Holder{
    static int init;
};
template <typename T> int Holder<T>::init = lala++;

//tool for user 
template <typename T> struct InitCRTP{ 
    using dummy=NonTypeParameter<Holder<T>::init>;
};

class WantInit : public InitCRTP<WantInit>{};//user register easily
int main(){
    std::cout << lala << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

是Visual C ++编译器错误还是一种未定义的行为?
如果它是Visual C ++错误,如何解决(同时仍保持美观)?

编辑:更改类->结构,建议使用Max Langhof(和很多人)。谢谢。

赏金原因

通过StoryTellerMaxim Egorushkin的相反解决方案以及他们的深入讨论(谢谢!),这听起来像是C ++规则的模糊领域。

如果是Visual C ++错误,我希望这个问题一定可以报告。

而且,我仍然希望有一个不错的解决方法,因为此技术对于自定义type-id生成非常有用。显式实例化不是那么方便。

注意:我授予Kaenbyou Rin赏金,因为对我来说,这很容易理解。
这并不意味着其余答案不太正确或用处不大。
我仍然不确定哪个是正确的。读者应谨慎行事。
为了安全起见,我假设我暂时无法使用该功能。感谢大家。

Sto*_*ica 6

肯定有一个编译器错误。我们可以通过稍作更改来验证它InitCRTP

template <typename T, typename = NonTypeParameter<Holder<T>::init>>
struct InitCRTP {
};
Run Code Online (Sandbox Code Playgroud)

现在,InitCRTP<T>必须使用任何专业化Holder<T>::init来确定第二个模板参数。这反过来应该强制实例化Holder<T>::init,但是VS不会实例化该实例

通常,使用CRTP类作为基础应该实例化该类内部的所有声明,包括的声明dummy。因此,这也应该起作用。

我们可以进一步验证。当用作基础时,成员函数的声明与类一起实例化:

template <typename T> struct InitCRTP{
    using dummy=NonTypeParameter<Holder<T>::init>;
    void dummy2(dummy);
};
Run Code Online (Sandbox Code Playgroud)

尽管如此,VC ++还是很固执。考虑到所有这些,以及Clang和GCC都表现出的行为,这是一个VC ++错误。


Max*_*kin 4

class WantInit : public InitCRTP<WantInit>既不实例化InitCRTP<WantInit>::dummy,也不因为它们没有被程序中实际Holder<WantInit>::init使用的东西引用。代码中的隐式实例化链不需要实例化,请参阅隐式实例化Holder<T>::init

这适用于类模板的成员:除非在程序中使用该成员,否则它不会被实例化,并且不需要定义。

解决方法是使用显式模板实例化

template struct Holder<void>;
Run Code Online (Sandbox Code Playgroud)

这会导致Holder<void>与其所有非模板成员一起被实例化。

或者,您可以仅实例化Holder<T>::init成员,例如:

static_cast<void>(Holder<void>::init);
Run Code Online (Sandbox Code Playgroud)

IMO、gcc 和 clang 过于渴望实例化未提及的事物。这种行为不会破坏或拒绝有效代码,因此这几乎不是一个错误,但依赖于这种特定行为的副作用是脆弱且不可移植的。