explicit template specialization from function template not working - c++

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. e.g.
auto iptr = test.GetCClass<MyClass::CInt>(i);
auto dptr = test.GetCClass<MyClass::CDouble>(d);
auto sptr = test.GetCClass<MyClass::CStr>(s);
LIVE

Related

C++ template function dependent typename not recognized

In the following mcve:
template <typename T> class Class { public: class MemberClass {}; };
#include <list>
template <typename T>
Class<T> func(const typename Class<T>::MemberClass& start,
const typename Class<T>::MemberClass& finish)
{
Class<T> result; return result;
}
int main ()
{
Class<int>::MemberClass i, j;
Class<int> L2; L2 = func(i, j);
return 0;
}
I find that the compiler does not recognize the function as something that can take the given arguments. In Visual Studio the error is
1>C:\...\main.cpp(15,25): error C2672: 'func': no matching overloaded function found
1>C:\...\main.cpp(15,34): error C2783: 'Class<T> func(const Class<T>::MemberClass &,const Class<T>::MemberClass &)': could not deduce template argument for 'T'
1>C:\...\main.cpp(6): message : see declaration of 'func'
In g++, here are the errors:
main.cpp:15:34: error: no matching function for call to 'func(Class<int>::MemberClass&, Class<int>::MemberClass&)'
15 | Class<int> L2; L2 = func(i, j);
| ^
main.cpp:6:10: note: candidate: 'template<class T> Class<T> func(const typename Class<T>::MemberClass&, const typename Class<T>::MemberClass&)'
6 | Class<T> func(const typename Class<T>::MemberClass& start,
| ^~~~
main.cpp:6:10: note: template argument deduction/substitution failed:
main.cpp:15:34: note: couldn't deduce template parameter 'T'
15 | Class<int> L2; L2 = func(i, j);
| ^ ^
I'm sure there's another way to write the function (it could return a MemberClass not a Class), but, well, this should be doable. How can I make that happen?
If you want to do it without specifying a template type for func, you can make the type of Class<T> known to the MemberClass and have the compiler resolve T within the func function template to Class<T>::MemberClass instead of the T within Class<T>.
This is very easy for the compiler, since it is the type of the arguments of func:
template <typename T>
class Class {
public:
class MemberClass { public: using ParentClass = Class<T>; };
};
#include <list>
template <typename T>
typename T::ParentClass func(const T& start,
const T& finish)
{
typename T::ParentClass result; return result;
}
int main ()
{
Class<int>::MemberClass i, j;
Class<int> L2; L2 = func(i, j);
return 0;
}
Quite literally, every possible T that matches your Class has a MemberClass type. The compiler is not going to look inside all of them to find a match, because it'd have to instantiate templates just to see their contents, and it would potentially match too many things. So the langauge simply doesn't look inside like that.
The way you can solve this is to tell the compiler what T is when you call it, here, providing an explicit int template argument:
func<int>(i, j);
In
template <typename T>
Class<T> func(const typename Class<T>::MemberClass&,
const typename Class<T>::MemberClass&);
T is not deducible (left of ::).
You have several options:
Make a friend non-template function inside MemberClass (will be found by ADL):
template <typename T> class Class {
public:
class MemberClass {
friend Class func(const MemberClass& start, const MemberClass& finish)
{
Class result; return result;
}
};
};
Demo
Be more generic of accepted type (you need here to give a way to retrieve Class from MemberClass):
template <typename T> class Class {
public:
class MemberClass {
public:
using ParentClass = Class;
};
};
template <typename T>
typename T::ParentClass func(const T& start, const T& finish)
{
typename T::ParentClass result; return result;
}
Demo

Template deduction failed

I've been trying to implement some generic wrappers (similar to std::span). However, I can't seem to get the template deduction/ conversion working. I'm not sure why. Below is a minimum "not-compiling" example
#include <array>
#include <iostream>
template<typename T>
class Wrapper
{
public:
using ElementType = T;
using ValueType = std::remove_cv<T>;
using Pointer = ElementType*;
constexpr Wrapper(const Wrapper& lhs):
mPtr(lhs.mPtr)
{}
template<typename U, std::size_t N>
Wrapper(const std::array<U,N>& lhs):
mPtr(lhs.data())
{}
template<typename U, std::size_t N>
Wrapper(std::array<U,N>& lhs):
mPtr(lhs.data())
{}
Pointer mPtr;
};
// Deduction guidelines
template <typename Type, std::size_t Size>
Wrapper(std::array<Type, Size>&)->Wrapper<Type>;
template <typename Type, std::size_t Size>
Wrapper(const std::array<Type, Size>&)->Wrapper<const Type>;
/// The function that is supposed convert array to the Wrapper
template<typename T>
void TestFunction(const Wrapper<T>& data)
{
// do something with the data
std::cout << data.mPtr;
}
// Try and use Test Function passing in an array
void LetsTest(std::array<float, 128>& data)
{
TestFunction(data);
}
Could someone please explain what I am missing or not understanding?
The compiler complaint is
#1 with ARM gcc 10.2.1 (none) : In function 'void LetsTest(std::array<float, 128>&)': :47:22: error: no matching
function for call to 'TestFunction(std::array<float, 128>&)' 47 |
TestFunction(data);
| ^ :38:6: note: candidate: 'template<class T, class U> void TestFunction(const Wrapper&)'
38 | void TestFunction(const Wrapper& data)
| ^~~~~~~~~~~~ :38:6: note: template argument deduction/substitution failed: :47:22: note:
'std::array<float, 128>' is not derived from 'const Wrapper'
47 | TestFunction(data);
| ^

Partial specialization of members

Trying to specialize member methods.
Reading this previous question: std::enable_if to conditionally compile a member function
I can quite understand what I am doing wrong.
#include <string>
#include <iostream>
#include <type_traits>
template<typename T>
class Traits
{
};
struct Printer
{
template<typename T>
typename std::enable_if<!std::is_function<decltype(Traits<T>::converter)>::value, void>::type
operator()(T const& object)
{
std::cout << object;
}
template<typename T>
typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type
operator()(T const& object)
{
std::cout << Traits<T>::converter(object);
}
};
template<>
class Traits<std::string>
{
public:
static std::size_t converter(std::string const& object)
{
return object.size();
}
};
int main()
{
using namespace std::string_literals;
Printer p;
p(5);
p("This is a C-string");
p("This is a C++String"s); // This compiles.
}
Compilation Gives:
> g++ -std=c++1z X.cpp
X.cpp:42:5: error: no matching function for call to object of type 'Printer'
p(5);
^
X.cpp:14:5: note: candidate template ignored: substitution failure [with T = int]: no member named 'converter' in 'Traits<int>'
operator()(T const& object)
^
X.cpp:20:5: note: candidate template ignored: substitution failure [with T = int]: no member named 'converter' in 'Traits<int>'
operator()(T const& object)
^
They both seem to fail because they can't see the method converter. But I am trying to use SFINE and std::enable_if to recognize that this function does not exist and thus only instantiate one of the methods.
The same error is generated for each of the types:
X.cpp:43:5: error: no matching function for call to object of type 'Printer'
p("This is a C-string");
^
X.cpp:14:5: note: candidate template ignored: substitution failure [with T = char [19]]: no member named 'converter' in 'Traits<char [19]>'
operator()(T const& object)
^
X.cpp:20:5: note: candidate template ignored: substitution failure [with T = char [19]]: no member named 'converter' in 'Traits<char [19]>'
operator()(T const& object)
^
Note: It compiles for the std::string version.
You could defer to a private helper function, and use overload resolution to prefer to the positively SFINAE-d overload - and not have a negatively SFINAE-d one:
struct Printer
{
template <class T>
void operator()(T const& object) {
call_impl(object, 0);
}
private:
// selected if Traits<T>::converter exists and is a function
// preferred in this case because int is better than ...
template<typename T>
typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type
call_impl(T const& object, int)
{
std::cout << Traits<T>::converter(object);
}
// selected if either Traits<T>::converter doesn't exist or isn't a function
template<typename T>
void call_impl(T const& object, ...)
{
std::cout << object;
}
};
One of the nice benefits that we'll get in C++2a with constraining functions is that we can do this without the extra helper:
struct Printer
{
template <class T>
requires std::is_function<decltype(Traits<T>::converter)>::value
void operator()(T const& object)
{
std::cout << Traits<T>::converter(object);
}
template <class T>
void operator()(T const& object)
{
std::cout << object;
}
};
The problem is in how SFINAE works. When substitution fails, the entire function is taken out of the program. So even though your predicate typename std::enable_if<!std::is_function<decltype(Traits<T>::converter)>::value, void>::type is meant to catch the false case, the non-existence of converter will cause the overload to be taken off the table.
The easiest workaround is something like this:
struct Printer
{
template<typename T>
void
impl(T const& object, ...)
{
std::cout << object;
}
template<typename T>
typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type
impl(T const& object, void*)
{
std::cout << Traits<T>::converter(object);
}
template<typename T>
void
operator()(T const& x)
{
return impl(x, nullptr);
}
};
Basically: You give the compiler something that will always work without using the predicate. The trick here is that nullptr will be matched to void* instead of ..., so it will do what you want.
If you want to get real fun about it, you can make a has_converter function whose return type is true_type or false_type and overload the implementation on that.
struct Printer
{
template<typename T>
std::false_type
has_converter(T const& object, ...);
template<typename T>
typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, std::true_type>::type
has_converter(T const& object, void*);
template<typename T>
void impl(T const& x, std::false_type)
{
std::cout << x;
}
template<typename T>
void impl(T const& x, std::true_type)
{
std::cout << Traits<T>::converter(x);
}
template<typename T>
void
operator()(T const& x)
{
return impl(x, decltype(has_converter(x, nullptr))());
}
};
One can imagine a helper function or templated constexpr bool to make using this property even easier (use the same technique as above).
template <typename T>
constexpr bool has_converter = ???;
How about adding non-function converter to non-specialized trait?
template<typename T>
class Traits
{
public: enum class Dummy{nothing};
public: static Dummy const converter = Dummy::nothing;
};
Run this code online

template class that derives from same template

I have an equivalent to following code:
struct Empty {
static constexpr int id = 0;
};
template <typename Self, typename Base = Empty> struct Compound : public Base
{
int get_id() const
{
return Self::id;
}
};
struct A : Compound<A>
{
static constexpr int id = 0xa;
};
struct B : Compound<B, A>
{
static constexpr int id = 0xb;
};
template <typename T, typename Base> int get_id(const Compound<T, Base> &c)
{
return c.get_id();
}
int test_a()
{
A var;
return get_id(var);
}
int test_b()
{
B var;
return get_id(var);
}
test_b doesn't compile with following error:
error: no matching function for call to 'get_id(B&)'
return get_id(var);
^
note: candidate: template<class T, class Base> int get_id(const Compound<T, Base>&)
template <typename T, typename Base> int get_id(const Compound<T, Base> &c)
^
note: template argument deduction/substitution failed:
note: 'const Compound<T, Base>' is an ambiguous base class of 'B'
return get_id(var);
I understand why that is. B is derived and is convertible to both Compound<B, A> and Compound<A, Empty>
I'm wondering if it is possible to change (within context of C++14) Compound template and get_id() function such that it would return 0xa for A and 0xb for B and would work for arbitrarily long chains of inheritance.
I know that this can be easily solved with virtual function that is overridden in A and B, but I would like to avoid that if possible. Everywhere these types are used they are known and fixed at compile time so there shouldn't be a need to incur run-time overhead.
Just keep it simple:
template <class T>
auto get_id(T const& c) -> decltype(c.get_id())
{
return c.get_id();
}
You don't need c to be some Compound, you really just want it to have a get_id() member function.
It's not clear from your post why need to go the route of Compound in get_id. You can simply use:
template <typename T> int get_id(T const& c)
{
return T::id;
}

SFINAE error on VS2013?

I've been trying just about anything I could think of to get the _CallWithRightmostArgsInner function to properly fail so that SFINAE could work properly and with this attempt, VS2013 has given me the error:
error C2039: 'type' : is not a member of 'std::enable_if<false,void>'
Any ideas? Is there an alternative that would be better? The idea here is that I would like to make a function call to Function provided that Function takes a number or parameters as denoted by NumArgs. The last two variadic arguments should be forwarded to the function and the result returned.
template <typename Function, int NumArgs>
class SplitParameters {
public:
typedef typename function_traits<Function>::result_type result_type;
template <typename ... RightArgs>
static result_type CallWithRightmostArgs(const Function& call, RightArgs && ... rightArgs) {
static_assert(sizeof...(RightArgs) >= NumArgs, "Unable to make function call with fewer than minimum arguments.");
return _CallWithRightmostArgs(call, std::forward<RightArgs>(rightArgs)...);
}
private:
template <typename ... RightArgs>
static result_type _CallWithRightmostArgs(const Function& call, RightArgs && ... rightArgs) {
return _CallWithRightmostArgsInner(call, std::forward<RightArgs>(rightArgs)...);
}
// note the '==' vs '!=' in these two functions. I would assume that only one could exist
template <typename LeftArg, typename ... RightArgs, typename std::enable_if<sizeof...(RightArgs) != NumArgs>::type* = 0>
static result_type _CallWithRightmostArgsInner(const Function& call, LeftArg, RightArgs && ... rightArgs) {
return _CallWithRightmostArgs(call, std::forward<RightArgs>(rightArgs)...);
}
template <typename LeftArg, typename ... RightArgs, typename std::enable_if<sizeof...(RightArgs) == NumArgs>::type* = 0>
static result_type _CallWithRightmostArgsInner(const Function& call, LeftArg, RightArgs && ... rightArgs) {
return call(std::forward<RightArgs>(rightArgs)...);
}
};
I got this working for g++-4.8 by changing your code to
#include <iostream>
template <class T>
struct function_traits
{
typedef void result_type;
};
template <typename Function, int NumArgs>
class SplitParameters {
public:
typedef typename function_traits<Function>::result_type result_type;
template <typename ... RightArgs>
static result_type CallWithRightmostArgs(const Function& call, RightArgs && ... rightArgs) {
static_assert(sizeof...(RightArgs) >= NumArgs,
"Unable to make function call with fewer than minimum arguments.");
return _CallWithRightmostArgs(call, std::forward<RightArgs>(rightArgs)...);
}
private:
template <typename ... RightArgs>
static result_type _CallWithRightmostArgs(const Function& call, RightArgs && ... rightArgs) {
return _CallWithRightmostArgsInner(call, std::forward<RightArgs>(rightArgs)...);
}
// note the '==' vs '!=' in these two functions. I would assume that only one could exist
template <typename LeftArg, typename ... RightArgs, class = typename std::enable_if<sizeof...(RightArgs) != NumArgs -1 >::type>
static result_type _CallWithRightmostArgsInner(const Function& call, LeftArg, RightArgs && ... rightArgs) {
return _CallWithRightmostArgsInner(call, std::forward<RightArgs>(rightArgs)...);
}
template <typename ... RightArgs, class = typename std::enable_if<sizeof...(RightArgs) == NumArgs>::type>
static result_type _CallWithRightmostArgsInner(const Function& call, RightArgs && ... rightArgs) {
return call(std::forward<RightArgs>(rightArgs)...);
}
};
void f(int i, int j)
{
std::cout << i << ' ' << j << std::endl;
}
int main()
{
SplitParameters<decltype(f), 2>::CallWithRightmostArgs(f, 1, 2, 3, 4);
}
The compiler didn't like you calling _CallWithRightmostArgs from _CallWithRightmostArgsInner, and I assumed you were actually trying to call the Inner function.
g++ also didn't like converting 0 to void* in the template parameter list so I changed that to just be class = enable_if<...>::type instead.
I didn't look into the reasons it was failing in detail though, hopefully this is good enough for you.
EDIT:
With regards to the typename enable_if<...>::type* = 0 being rejected, I remembered that there is a similar issue with std::array:
template <class T, int size>
void f(const std::array<T,size>&){}
This little snippet compiles just fine on its own, but when you do:
std::array<int,4> a;
f(a);
g++ gives:
test3.cpp: In function ‘int main()’:
test3.cpp:9:8: error: no matching function for call to ‘f(std::array<int, 4ul>&)’
f(a);
^
test3.cpp:9:8: note: candidate is:
test3.cpp:4:6: note: template<class T, int size> void f(const std::array<T, size>&)
void f(const std::array<T,size>&){}
^
test3.cpp:4:6: note: template argument deduction/substitution failed:
test3.cpp:9:8: note: mismatched types ‘int’ and ‘#‘integer_cst’ not supported by dump_type#<type error>’
f(a);
^
test3.cpp:9:8: note: ‘std::array<int, 4ul>’ is not derived from ‘const std::array<T, size>’
The problem, as it turns out, is I declared the template as taking an int for the size parameter, but what the compiler got was a std::size_t which is not the same as an int and even though you can convert between them easily.
In the example above, I can't even replace = 0 with = NULL because that's just a 0L literal, I would have to do = (void*)0 to get the compiler to accept it (because the default type of enable_if<true>::type is void).