忽略c++中模板类的重复显式实例化

Ignoring duplicate explicit instantiations of template classes in C++

本文关键字:实例化 c++ 忽略      更新时间:2023-10-16

如果我有一个class:

template <typename T>
class MyClass
{ 
 // ...
};

,我显式地实例化了它:

template class MyClass<int>;
template class MyClass<int>; // second time

我在一些编译器上得到一个错误(例如Clang,但在vc++ 2010上没有)。我为什么要这么做?在某些情况下,T可能是另一种类型的typedef

template class MyClass<my_type_1>;
template class MyClass<my_type_2>;

对于某些构建选项,my_type_1my_type_2相同,而在其他情况下则不同。如何确保上述代码在所有场景下都能编译?有没有一种方法可以忽略重复的实例化?

您可以找到另一种方式来显式实例化template,以一种您可以在其上进行元编程的方式。

然后在一个包中执行它们,而不是每行执行一个实例化。在它们上运行n^2算法(在编译时)以消除重复(或者,老实说,您可能可以跳过这个:取决于您如何实例化模板,它可能不在乎)。

类似这样,假设Instantiate< Template, types< blah, foo, bar > >实际上在作为第一个参数传入的模板上实例化了列表:

#include <utility>
#include <type_traits>
template<typename T>
struct Test {};
template<typename... Ts>
struct types {};
template<template<typename>class Template, typename Types>
struct Instantiate {};
template<template<typename>class Template, typename T0, typename... Ts>
struct Instantiate<Template, types<T0, Ts...>>:
  Instantiate<Template, types<Ts...>>
{
  Template<T0>& unused();
};
template<typename U, typename Types>
struct prepend;
template<typename U, template<typename...>class pack, typename... Ts>
struct prepend< U, pack<Ts...> > {
  typedef pack<U, Ts...> types;
};
template<typename U, typename Types>
using Prepend = typename prepend<U, Types>::types;
template<typename U, typename Types, typename=void>
struct remove_type_from_types;
template<typename U, template<typename...>class pack>
struct remove_type_from_types<U, pack<>, void>
{
  typedef pack<> types;
};
template<typename U, template<typename...>class pack, typename T0, typename... Ts>
struct remove_type_from_types< U, pack<T0, Ts...>,
  typename std::enable_if< std::is_same<U, T0>::value >::type
>: remove_type_from_types< U, pack<Ts...> >
{};
template<typename U, template<typename...>class pack, typename T0, typename... Ts>
struct remove_type_from_types< U, pack<T0, Ts...>,
  typename std::enable_if< !std::is_same<U, T0>::value >::type
>
{
  typedef Prepend< T0, typename remove_type_from_types< U, pack<Ts...> >::types > types;
};
template<typename Types>
struct remove_duplicates {
  typedef Types types;
};
template<template<typename...>class pack, typename T0, typename... Ts>
struct remove_duplicates<pack<T0, Ts...>> {
private:
  typedef typename remove_type_from_types< T0, pack<Ts...> >::types filtered_tail;
  typedef typename remove_duplicates< filtered_tail >::types unique_tail;
public:
  typedef Prepend< T0, unique_tail > types;
};
template<typename Types>
using RemoveDuplicates = typename remove_duplicates<Types>::types;
static Instantiate<Test, RemoveDuplicates<types<int, double>> > unused;
int main() {
}

如前所述,由于我实例化template的使用方式,您可能会取消整个消除重复位。我也不确定上面每个template的使用是否足以实例化它(即,它不会以某种方式被优化,并且符号将被导出)。

(递归深度在类型数量上是n,完成的总功在类型数量上是n^2:我认为这对于任何合理数量的类型来说都足够浅,足够快。由于缺乏裸类型上的弱排序,更花哨的独特类型移除是困难的…)

不要专门化类型,而是专门化相关的底层类型(如int)。这样,你就可以随心所欲地进行多次/几次类型定义,而且你总是能得到你想要的专门化。

您可以为您的配置定义一个预处理器标志,然后将模板放入#ifdef块中。

牦牛救了我的命。实际上,我有很多在编译时计算参数的非类型模板函数依赖于一些"#define"。我不会事先知道它们的值,我想显式地实例化它们。但是当它们发生冲突时,编译器会停止并报错"duplicate…"。所以我遵循雅克的想法,做了一个非常简单的版本。我把它贴在这里给可能感兴趣的人。

文件1:instantiate.h

#ifndef INC_INSTANTIATE_H
#define INC_INSTANTIATE_H
#include <iostream>
#include <utility>
#include <type_traits>
using namespace std;
template<unsigned short base> void func();
template<unsigned short base>
struct test {
    test() {
        cout << "Class " << base << " instantiated.n";
        func<base>();
    }
    void instantiate_me(){};
};
template<typename... Ts>
struct instances {};
template<typename Types>
struct Instantiate {};
template<typename T0, typename... Ts>
struct Instantiate<instances<T0, Ts...>> : Instantiate<instances<Ts...>>
{
    T0 unused;
};
#endif

文件2:instantiate.cpp

#include <iostream>
#include "instantiate.h"
using namespace std;
static Instantiate<instances<test<1>, test<2>, test<3>, test<4>>> unused;
template<unsigned short base>
void func() {
    cout << "Function " << base << " instantiated.n";
}
static int initialize() {
    unused.unused.instantiate_me();
    return 0;
}
static int dummy = initialize();

文件3:main.cpp

#include <iostream>
#include "instantiate.h"
using namespace std;
int main() {
    cout << "Good day commander!n";
}

它可以完美地与g++ 8.0.1 ubuntu 18.04 hyper-v windows 10。这里我没有需要关心模板复制,因为我显式地使用隐式实例化,在一个文件中永远不会有复制。所有函数的定义都在一个文件中,我可以在构造函数中实例化它类"test"在文件instantiate.h。对于多个文件,我为每个文件使用一个Instantiate、一个initialize和一个dummy,并可能使用类"test"的多个实现。

使用extern模板语法可以解决这个问题,示例:

 extern template int max (int, int);