C++ 编译时模板实例化检查

C++ 编译时模板实例化检查,c++,templates,c++11,typetraits,C++,Templates,C++11,Typetraits,是否可以检查模板类型是否已在编译时实例化,以便我可以在enable_if专门化中使用此信息 假设我有 template <typename T> struct known_type { }; 模板结构已知_类型{}; 如果已知类型在编译时实例化,我是否可以定义一些值为true的is_known_类型?没有办法做到这一点。所以我想说:不。不,不可能对未实例化的类进行编译时检查。但是,您可以建立实例化类的(静态)映射(在调试构建中),您可以在运行时进行检查 但是,通过比较预期实例化类

是否可以检查模板类型是否已在编译时实例化,以便我可以在enable_if专门化中使用此信息

假设我有

template <typename T> struct known_type { };
模板结构已知_类型{};

如果已知类型在编译时实例化,我是否可以定义一些值为true的is_known_类型?

没有办法做到这一点。所以我想说:不。

不,不可能对未实例化的类进行编译时检查。但是,您可以建立实例化类的(静态)映射(在调试构建中),您可以在运行时进行检查


但是,通过比较预期实例化类和实际实例化类的列表来分析链接的二进制文件应该是可能的(但这已经过了编译时间,也超出了我的知识范围).

如果您利用这样一个事实,即特定表达式可能会或可能不会在需要
constexpr
s的地方使用,并且您可以查询以查看每个候选对象的状态,则可以这样做。特别是在我们的例子中,没有定义的
constepr
s不能作为常量表达式传递,而
noexcept
是常量表达式的保证。因此,
noexcept(…)
返回
true
表示存在正确定义的
constexpr

本质上,这将
constexpr
s视为是/否开关,并在编译时引入状态

请注意,这几乎是一种黑客行为,您将需要针对特定编译器的变通方法(请参阅前面的文章),并且该标准的未来版本可能会认为这种特定的
friend
实现形式不正确

有了它的方式

用户以专门针对它的方式呈现此概念

他的示例实现是,引用了以下解释:

constexpr int flag (int);
constexpr函数可以处于两种状态之一;要么是 在常量表达式中可用,或者如果缺少 定义它自动属于后一类-没有 其他状态(除非我们考虑未定义的行为)。 通常情况下,constexpr函数的处理方式应与它们的处理方式完全相同 是函数,但我们也可以将它们视为 “变量”的类型类似于bool,其中每个“变量”都可以 有两种价值观之一;可用或不可用

在我们的节目中,如果你认为国旗就是这样的话,那就很有帮助了。把手 (不是功能)。原因是我们永远不会真正调用flag 在已评估的上下文中,我们只对其当前状态感兴趣


我把这个应用于你的特殊问题。其思想是使用
constepr
Yes/No开关来指示类型是否已实例化。因此,对于每种类型,您都需要一个单独的开关

template<typename T>
struct inst_check_wrapper
{
    friend constexpr int inst_flag(inst_check_wrapper<T>);
};
模板
结构安装检查包装器
{
friend constexpr int inst_标志(inst_check_包装器);
};
inst\u check\u wrapper
本质上是为任何类型的开关包装开关。这只是原始示例的一般版本

template<typename T>
struct writer 
{
    friend constexpr int inst_flag(inst_check_wrapper<T>) 
    {
        return 0;
    }
};
模板
结构编写器
{
friend constexpr int inst_标志(inst_check_包装器)
{
返回0;
}
};
开关切换器与原始示例中的相同。它为您使用的某种类型的开关提供了定义。为便于检查,请添加辅助开关检查器:

template <typename T, bool B = noexcept(inst_flag(inst_check_wrapper<T>()))>
constexpr bool is_instantiated()
{
    return B;
}
模板
constexpr bool已实例化()
{
返回B;
}
最后,类型“注册”自身为已初始化。就我而言:

template <typename T>
struct MyStruct
{
    template <typename T1 = T, int = sizeof(writer<MyStruct<T1>>)>
    MyStruct()
    {}
};
模板
结构MyStruct
{
模板
MyStruct()
{}
};
只要请求特定构造函数,开关就会打开。样本:

int main () 
{
    static_assert(!is_instantiated<MyStruct<int>>(), "failure");
    MyStruct<int> a;
    static_assert(is_instantiated<MyStruct<int>>(), "failure");
}
int main()
{
静态断言(!is_instanced(),“failure”);
我的结构a;
静态断言(是实例化的(),“失败”);
}

您能否在启用条件中显示一个使用此类信息的示例?接受的答案在较新的编译器上似乎不起作用。似乎答案取决于某些GCC编译器错误,而解决方案在符合标准的编译器上不起作用。我的问题是解决结构的重叠部分专门化,我有一个专门化,应该只应用于已知类型,直到编译时才真正知道,因为我正在编写一个模板库。我在不同的编译器上检查了您的“概念证明”。它适用于较旧的编译器:C++11模式下的GCC4.7、4.8和4.9。但是,它不适用于较新的编译器gcc 5.2和clang 3.6。我检查了所有模式:C++11、C++14和C++17。似乎在实例化时并没有决定默认模板参数,而是在第一次写出默认模板参数时决定。这是有道理的,因为所有其他的都会导致关于一个定义规则(ODR)的奇怪问题。@RalphTandetzky如果所有涉及的函数都是
constepr
,这意味着
内联的
,为什么实现ODR会有问题?
template<typename T>
struct inst_check_wrapper
{
    friend constexpr int inst_flag(inst_check_wrapper<T>);
};
template<typename T>
struct writer 
{
    friend constexpr int inst_flag(inst_check_wrapper<T>) 
    {
        return 0;
    }
};
template <typename T, bool B = noexcept(inst_flag(inst_check_wrapper<T>()))>
constexpr bool is_instantiated()
{
    return B;
}
template <typename T>
struct MyStruct
{
    template <typename T1 = T, int = sizeof(writer<MyStruct<T1>>)>
    MyStruct()
    {}
};
int main () 
{
    static_assert(!is_instantiated<MyStruct<int>>(), "failure");
    MyStruct<int> a;
    static_assert(is_instantiated<MyStruct<int>>(), "failure");
}