C++ 约束API模板类型

C++ 约束API模板类型,c++,templates,c++11,C++,Templates,C++11,我的类中有一个名为template T Get(/*stuff*/)的模板API函数。我的源文件为T类型的特定列表实现此功能。如果用户希望使用我尚未实现的类型,那么我希望结果是编译错误,而不是链接器错误。我还不太关心编译消息。以下是到目前为止我得到的信息: MyClass.h #pragma once #define API_TYPE(X) \ template<> struct Implemented<X> : public API<X> {} na

我的类中有一个名为
template T Get(/*stuff*/)的模板API函数。我的源文件为
T
类型的特定列表实现此功能。如果用户希望使用我尚未实现的类型,那么我希望结果是编译错误,而不是链接器错误。我还不太关心编译消息。以下是到目前为止我得到的信息:

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);
    }
};
#pragma一次
#定义API_类型(X)\
已实现模板结构:公共API{}
名称空间MyClassAPI
{
模板结构API
{
静态T得到(常数T&);
};
实现的模板结构{};
API_类型(bool);
}
类MyClass
{
模板友元结构MyClassAPI::API;
公众:
模板T获取(常量T&T)常量
{
返回MyClassAPI::Implemented::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?
}
#包括“MyClass.h”
名称空间MyClassAPI
{
模板tapi::Get(const T&T){return T;}
//模板结构API//为什么我需要这个?
}
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.h”
#包括
#包括
使用名称空间std;
//主文件
int main(){
MyClass c;

CUT< P>你的模板的问题是你在一个单独的翻译单元中定义它的成员,因此它们对Meal.CPP不可见,C++不支持模板的单独翻译。 当您使用
template struct API;
时,您要求编译器显式地实例化
T=bool
API
。但是,在这样做时,您还应该让其他翻译单元知道实例化发生在其他地方,方法是在头文件中使用模板decla的类似指令配给:

extern template struct API<bool>; 
现在,既然
中,我们就可以使用
静态断言
并明确指定模板的可行类型:

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&);
};
模板结构API
{
//仅允许使用API和API
静态_断言(是_in::value,“API的模板类型无效”);
静态T得到(常数T&);
};

你几乎就在那里了

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

在.h文件中,添加:

// Explicit instantiations
namespace MyClassAPI
{
   template<> int API<int>::Get(const int&);
   template<> double API<double>::Get(const double&);
}
// 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;
   }
}
更新

这是一个多文件版本:

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&);
}
#pragma一次
#定义API_类型(X)\
已实现模板结构:公共API{}
名称空间MyClassAPI
{
实现了模板结构;
模板结构API
{
静态T得到(T常数&);
};
API_类型(int);
API_型(双);
}
类MyClass
{
模板友元结构MyClassAPI::API;
公众:
模板T获取(常量T&T)常量
{
返回MyClassAPI::Implemented::Get(t);
}
};
//显式实例化
名称空间MyClassAPI
{
模板INTAPI::Get(const int&);
模板双API::Get(constdouble&);
}
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;
   }
}
#包括“MyClass.h”
//实现显式实例化
名称空间MyClassAPI
{
模板intapi::Get(const int&in)
{
返回2*in;
}
模板双API::Get(constdouble&in)
{
返回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;
}
#包括
#包括“MyClass.h”
int main()
{
我的a级;

std::cout“将定义移动到头文件”在实现
的定义之前,我尝试将
模板结构API;
移动到头文件,但仍然出现相同的链接器错误。我建议移动
模板T API::get(const T&T){return T;}“/Cord>”到头文件,而不是显式实例化。显式实例化属于C++文件,也应在头文件中显式地声明特定的<代码> T 的实例化发生在另一个文件中:外部模板模板结构API;< /Cord> HMM,而不是因为实际函数G。et相当复杂,只调用实现文件中定义的函数。顺便问一下,
方法中的
is_不是也有同样的问题吗,我需要列出两次接受的类型(假设我不移动
获取
的定义)如果您缺少
extern-template-struct-API;
声明,请将它与模板一起添加到头文件中,让编译器和链接器知道
Get
是在其他地方定义的。总之,如果您没有做
template/extern-template
之类的工作,您应该为您类型的每个用户提供定义对于
Get
。到目前为止,您只是声明它,而不是定义它。
main.cpp
中的代码没有
Get
的定义,因此链接器无法找到它。如果您想将
Get
的定义保留在单独的翻译单元中,并且定义它的类型集非常窄,那么您应该有一个对于头文件和.cpp文件中的每种类型,相应地有一对
extern-template
template
声明。单文件版本不算,因为如果我的代码也都在一个文件中,它就可以工作。你必须确保将显式实例化分开,以确保它们实际上是这样做的。@Suedocode,我添加了多文件版本,结果是一样的。此外,它仍然列出了两次实现的类型,但更详细。我认为除了宏魔法之外,没有什么能满足我的要求。我想你不是想在头文件中有
显式实例化
?@Suedocode,是的,我有。这告诉编译器软管功能被实例化并在其他地方实现。
20
100
#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&);
}
#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;
   }
}
#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;
}