约束API模板类型整齐

时间:2016-01-25 20:59:15

标签: c++ templates c++11

我的班级中有一个名为template<typename T> T Get(/*stuff*/);的模板API函数。我的源文件为T类型的某个列表实现了此函数。如果用户想要使用我尚未实现的类型,那么我希望结果是 compile 错误,而不是链接器错误。我还不太关心编译信息。这是我到目前为止所得到的:

MyClass.h

#pragma once

#define API_TYPE(X) \
  template<> struct Implemented<X> : public API<X> {}

namespace MyClassAPI
{
  template<typename T> struct API
  {
    static T Get(const T&);
  };

  template<typename T> struct Implemented {};
  API_TYPE(bool);
}

class MyClass
{
  template<typename T> friend struct MyClassAPI::API;

  public:
    template<typename T> T Get(const T& t) const
    {
      return MyClassAPI::Implemented<T>::Get(t);
    }
};

MyClass.cpp

#include "MyClass.h"

namespace MyClassAPI
{
  template<typename T> T API<T>::Get(const T& t) { return t; }
  //template struct API<bool> //Why do I need this?
}

的main.cpp

#include "MyClass.h"
#include <iostream>
#include <cassert>

using namespace std;

// Main File
int main() {
  MyClass c;
  cout << "Getting true: " << c.Get(true) << endl;
  return 0;
}

所以我的问题是关于MyClass.cpp中的一行。为什么我需要使用API<bool>在源文件中复制template struct API<bool>;显式声明?当Implemented<bool> : public API<bool>继承它时,它不应该知道从头文件的声明中扩展模板函数定义吗?

另外,有没有办法在不声明我接受的类型列表两次的情况下执行此操作?

没有这行的错误:

g++ -Wfatal-errors -Werror -std=c++11 -g -O0 -Wall -c MyClass.cpp -o MyClass.o
g++ -Wfatal-errors -Werror -std=c++11 -g -O0 -Wall test.cpp MyClass.o -o test
/tmp/ccVxp4F3.o: In function `bool MyClass::Get<bool>(bool const&) const':
MyClass.h:25: undefined reference to `MyClassAPI::API<bool>::Get(bool const&)'
collect2: error: ld returned 1 exit status
make: *** [test] Error 1

2 个答案:

答案 0 :(得分:2)

您的模板存在的问题是您在单独的翻译单元中定义了它的成员,因此它们对main.cpp不可见,并且C ++不支持单独的模板翻译。< / p>

当您使用template struct API<bool>;时,请求编译器为API<T> 显式实例化 T = bool。但是,在这样做时,您还应该让其他翻译单元知道实例化发生在其他地方,方法是在头文件中使用模板声明使用类似的指令:

extern template struct API<bool>; 

否则,将Get的定义移到头文件中或在头文件中包含.cpp文件(不推荐),并依赖编译器为您实例化模板成员。

关于限制可行模板参数列表,我建议一种基于模板元编程的方法 首先,我们定义一些工具来检查类型是否属于类型列表,让我们称之为is_in<DesiredType, ViableTypes...>。它将接受要查找的类型T作为模板参数,并接受用于搜索的类型InTypes...的列表,并通过编译时可用的静态布尔成员提供结果。 它在列表上实现为一个简单的递归算法,在DesiredType之前检查列表的每个元素,直到找到DesiredType或满足列表的结尾:

#include <type_traits>

template<typename...> struct is_in;

template<typename T, typename InType, typename... InTypes> struct is_in<T, InType, InTypes...> {
    static constexpr bool value = std::is_same_t<T, InType> ||
        is_in<T, InTypes...>::value;
}

template<typename T> struct is_in<T> {
    static constexpr bool value = false;
}

现在,拥有is_in,我们可以使用static_assert并明确指定模板的可行类型:

template<typename T> struct API
{
    // Only allow API<bool> and API<int>
    static_assert(is_in<T, bool, int>::value, "invalid template type for API<T>");

    static T Get(const T&);
};

答案 1 :(得分:1)

你差不多了。

您需要更新MyClass.h以提供几个函数的显式实例化,并在MyClass.cpp中实现它们。

在.h文件中,添加:

// Explicit instantiations
namespace MyClassAPI
{
   template<> int API<int>::Get(const int&);
   template<> double API<double>::Get(const double&);
}

在.cpp文件中,添加:

// Implement the explicit instantiations

namespace MyClassAPI
{
   template<> int API<int>::Get(const int& in)
   {
      // Add whatever logic that makes sense for this type.
      return 2*in;
   }

   template<> double API<double>::Get(const double& in)
   {
      // Add whatever logic that makes sense for this type.
      return 10*in;
   }
}

这是工作代码的单个文件版本:

#define API_TYPE(X) \
  template<> struct Implemented<X> : public API<X> {}

namespace MyClassAPI
{
  template<typename T> struct Implemented;

  template<typename T> struct API
  {
    static T Get(T const&);
  };

  API_TYPE(int);
  API_TYPE(double);
}

class MyClass
{
  template<typename T> friend struct MyClassAPI::API;

  public:
    template<typename T> T Get(const T& t) const
    {
      return MyClassAPI::Implemented<T>::Get(t);
    }
};

// Explicit instantiations
namespace MyClassAPI
{
   template<> int API<int>::Get(const int&);
   template<> double API<double>::Get(const double&);
}

#include <iostream>

int main()
{
   MyClass a;

   std::cout << a.Get<int>(10) << std::endl;
   std::cout << a.Get<double>(10) << std::endl;

   // Does not work. Produces compiler error.
   // std::cout << a.Get<float>(10) << std::endl;
}


// Implement the explicit instantiations

namespace MyClassAPI
{
   template<> int API<int>::Get(const int& in)
   {
      return 2*in;
   }

   template<> double API<double>::Get(const double& in)
   {
      return 10*in;
   }
}

输出:

20
100

<强>更新

这是一个多文件版本:

MyClass.h:

#pragma once

#define API_TYPE(X) \
  template<> struct Implemented<X> : public API<X> {}

namespace MyClassAPI
{
  template<typename T> struct Implemented;

  template<typename T> struct API
  {
    static T Get(T const&);
  };

  API_TYPE(int);
  API_TYPE(double);
}

class MyClass
{
  template<typename T> friend struct MyClassAPI::API;

  public:
    template<typename T> T Get(const T& t) const
    {
      return MyClassAPI::Implemented<T>::Get(t);
    }
};

// Explicit instantiations
namespace MyClassAPI
{
   template<> int API<int>::Get(const int&);
   template<> double API<double>::Get(const double&);
}

MyClass.cc:

#include "MyClass.h"

// Implement the explicit instantiations
namespace MyClassAPI
{
   template<> int API<int>::Get(const int& in)
   {
      return 2*in;
   }

   template<> double API<double>::Get(const double& in)
   {
      return 10*in;
   }
}

main.cc:

#include <iostream>
#include "MyClass.h"

int main()
{
   MyClass a;

   std::cout << a.Get<int>(10) << std::endl;
   std::cout << a.Get<double>(10) << std::endl;

   // Does not work.
   // std::cout << a.Get<float>(10) << std::endl;
}

它也可以成功构建并产生相同的结果。