简体   繁体   中英

explicit template specialization from function template not working

I am trying to do explicit specialization for a template function called from another template function. Following is a minimum non-working example and I am trying to implement the following idea:

CInt , CDouble and CStr are equivalent of operations that I need to perform. But, CStr constructor expects a little different format. MyClass is equivalent of a factory, which when requested will return one of the instances of CInt , CDouble or CStr .

The motivation for this structure: Assume that GetCClass function is called from a function with ~100 lines and only one difference: the type of class. The values returned from GetCClass have same APIs.

#include <iostream>
#include <memory>
#include <string>

using namespace std;

class CStrArg {
 public:
  const char* a;
  int size;
};

class MyClass {
 public:
  class CStr;
  class CInt;
  class CDouble;

  template <typename T>
  typename T::Ptr GetCClass(typename T::ArgType arg);

  template <typename T>
  typename T::Ptr GetCClassInternal(typename T::ArgType arg);
};

class MyClass::CInt {
 public:
  typedef int ArgType;
  typedef shared_ptr<CInt> Ptr;

  static Ptr CreatePtr(ArgType i) { return Ptr(new CInt(i)); }

 private:
  CInt(ArgType i) : i_(i) {}
  ArgType i_;
};

class MyClass::CDouble {
 public:
  typedef double ArgType;
  typedef shared_ptr<CDouble> Ptr;

  static Ptr CreatePtr(ArgType d) { return Ptr(new CDouble(d)); }

 private:
  CDouble(ArgType i) : i_(i) {}
  ArgType i_;
};

class MyClass::CStr {
 public:
  typedef CStrArg ArgType;
  typedef shared_ptr<CStr> Ptr;

  static Ptr CreatePtr(string s) { return Ptr(new CStr(s)); }

 private:
  CStr(string i) : i_(i) {}
  string i_;
};


//template definition
template <typename T>
typename T::Ptr MyClass::GetCClass(typename T::ArgType arg) {
  return GetCClassInternal(arg);
}

template <typename T>
typename T::Ptr MyClass::GetCClassInternal(typename T::ArgType arg) {
  cout << "GetCClass for all types but one" << endl;
  return T::CreatePtr(arg);
}

template <>
MyClass::CStr::Ptr MyClass::GetCClassInternal<MyClass::CStr>(CStrArg arg) {
  return CStr::CreatePtr(arg.a);
}

int main() {
  MyClass test;
  int i = 5;
  double d = 1.2;
  CStrArg s;
  s.a = "why me";
  s.size = 6;

  auto iptr = test.GetCClass(i);
  auto dptr = test.GetCClass(d);
  auto sptr = test.GetCClass(s);

  return 0;
}

I get the following error:

experimental/amandeep/proto_test/fn_template_sp.cc:88:31: note: candidate is:
experimental/amandeep/proto_test/fn_template_sp.cc:20:19: note: template<class T> typename T::Ptr MyClass::GetCClass(typename T::ArgType)
   typename T::Ptr GetCClass(typename T::ArgType arg);
                   ^
experimental/amandeep/proto_test/fn_template_sp.cc:20:19: note:   template argument deduction/substitution failed:
experimental/amandeep/proto_test/fn_template_sp.cc:88:31: note:   couldn't deduce template parameter ‘T’
   auto iptr = test.GetCClass(i);
                               ^
experimental/amandeep/proto_test/fn_template_sp.cc:89:31: error: no matching function for call to ‘MyClass::GetCClass(double&)’
   auto dptr = test.GetCClass(d);
                               ^
experimental/amandeep/proto_test/fn_template_sp.cc:89:31: note: candidate is:
experimental/amandeep/proto_test/fn_template_sp.cc:20:19: note: template<class T> typename T::Ptr MyClass::GetCClass(typename T::ArgType)
   typename T::Ptr GetCClass(typename T::ArgType arg);
                   ^
experimental/amandeep/proto_test/fn_template_sp.cc:20:19: note:   template argument deduction/substitution failed:
experimental/amandeep/proto_test/fn_template_sp.cc:89:31: note:   couldn't deduce template parameter ‘T’
   auto dptr = test.GetCClass(d);
                               ^
experimental/amandeep/proto_test/fn_template_sp.cc:90:31: error: no matching function for call to ‘MyClass::GetCClass(CStrArg&)’
   auto sptr = test.GetCClass(s);
                               ^
experimental/amandeep/proto_test/fn_template_sp.cc:90:31: note: candidate is:
experimental/amandeep/proto_test/fn_template_sp.cc:20:19: note: template<class T> typename T::Ptr MyClass::GetCClass(typename T::ArgType)
   typename T::Ptr GetCClass(typename T::ArgType arg);
                   ^
experimental/amandeep/proto_test/fn_template_sp.cc:20:19: note:   template argument deduction/substitution failed:
experimental/amandeep/proto_test/fn_template_sp.cc:90:31: note:   couldn't deduce template parameter ‘T’
   auto sptr = test.GetCClass(s);

I have read multiple answers, but I cannot understand why this is not working. Any help is appreciated.

EDIT: I cannot understand, but locally in my actual code I get the following:

/home/workspace/main/util/storage/smb2_proxy/smb2_proxy.cc:239:29: error: template-id ‘CreateOp<storage::smb2_proxy::Smb2Proxy::PurgeTaskOp>’ for ‘storage::smb2_proxy::Smb2Proxy::PurgeTaskOp::Ptr storage::smb2_proxy::Smb2Proxy::CreateOp(std::shared_ptr<storage::smb2_proxy::Smb2Proxy::TaskState>,storage::smb2_proxy::Smb2Proxy::PurgeTaskOp::ArgType&,storage::smb2_proxy::Smb2Proxy::PurgeTaskOp::ResultType*,storage::smb2_proxy::Smb2Proxy::DoneCb)’ does not match any template declaration
 Smb2Proxy::PurgeTaskOp::Ptr Smb2Proxy::CreateOp<Smb2Proxy::PurgeTaskOp>(
                             ^~~~~~~~~
In file included from /home/workspace/main/util/storage/smb2_proxy/smb2_proxy.cc:5:0:
/home/workspace/main/util/storage/smb2_proxy/smb2_proxy.h:160:20: note: candidate is: template<class Op> typename Op::Ptr storage::smb2_proxy::Smb2Proxy::CreateOp(std::shared_ptr<storage::smb2_proxy::Smb2Proxy::TaskState>, const typename Op::ArgType&, typename Op::ResultType*,storage::smb2_proxy::Smb2Proxy::DoneCb)
   typename Op::Ptr CreateOp(std::shared_ptr<TaskState> task_state,

CreateOp -> GetCClassInternal (both are equivalent) The compiler is not able to take specialization of CreateOp and complains that it does not match any declaration.

PS: I had another question in which I had made a mistake while posting the code. I have deleted the question and am reposting it.

The problem is that the template parameter T of GetCClass (and GetCClassInternal ) is used in non-deduced contexts , it can't be deduced.

If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

1) The nested-name-specifier (everything to the left of the scope resolution operator :: ) of a type that was specified using a qualified-id:

You can specify the template argument explicitly. eg

auto iptr = test.GetCClass<MyClass::CInt>(i);
auto dptr = test.GetCClass<MyClass::CDouble>(d);
auto sptr = test.GetCClass<MyClass::CStr>(s);

LIVE

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM