Templates: Template Function Not Playing Well with Class's Template Member Function

Templates: template function not playing well with class's template member function

When you refer to a template that is a member of dependent type, you have to prepend it with a keyword template. This is how the call to getResult inside printStuff should look

size_t value = a.template getResult<B>();

This is similar to using the keyword typename when referring to nested typenames in a dependent type. For some reason, the bit about typename with nested types is rather well-known, but the similar requirement for template with nested templates is relatively unknown.

Note that the general syntax structure is a bit different though. The typename is always put in front of the full name of the type, while template is inserted in the middle.

Again, this is only necessary when you are accessing a template member of a dependent type, which in the above example would be A in printStuff. When you call foo.getResult<> in main the type of foo is not dependent, so there's no need to include the template keyword.

Template class with invalid member functions

Yes, in general. Non-virtual member functions of class templates are themselves function templates, and like all function templates they only get instantiated when used. So if you never use some member function of a class template specialization, member function need not be valid for that specialization.

Since C++11, the standard library actually makes ample use of this fine-grained instantiation control. Type requirements for containers apply to member functions, not to the entire template, so for example you can have an std::map<K, T> where T is not default-constructible; you just cannot call operator[] on it.

Note that explicit class template instantiation instantiates all the member functions.

Template member function specialization in a template class

Why does this member function specialization get error?

When you instantiate the template class A for example A<std::vector<int>>, the template parameter T is equal to std::vector<int>, not std::vector<T>, and this a specialization case of the function. Unfortunately this can not be done with member functions as mentioned in the comments.



Are there some better solutions?

Yes; In c++17 you could use if constexpr with a trait to check the std::vector, like this.

#include <type_traits> // std::false_type, std::true_type
#include <vector>

// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};

template<typename T>
class A /* final */
{
T mData;

public:
// ...constructor

void print() const /* noexcept */
{
if constexpr (is_std_vector<T>::value) // when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else // for types other than `std::vector<>`
{
std::cout << mData << std::endl;
}
}
};

(See Live Online)

This way you keep only one template class and the print() will instantiate the appropriate part according to the template type T at compile time.


If you don not have access to C++17, other option is to SFINAE the members(Since c++11).

#include <type_traits> // std::false_type, std::true_type, std::enbale_if
#include <vector>

// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};

template<typename T>
class A /* final */
{
T mData;

public:
// ...constructor

template<typename Type = T> // when T == `std::vector<>`
auto print() const -> typename std::enable_if<is_std_vector<Type>::value>::type
{
for (const auto element : mData)
std::cout << element << "\n";
}

template<typename Type = T> // for types other than `std::vector<>`
auto print() const -> typename std::enable_if<!is_std_vector<Type>::value>::type
{
std::cout << mData << std::endl;
}
};

(See Live Online)



What if I have more other data types like self-define vector classes
or matrices? Do I have to define many is_xx_vector?

You can check the type is a specialization of the provided one like as follows. This way you can avoid providing many traits for each type. The is_specialization is basically inspired from this post

#include <type_traits> // std::false_type, std::true_type
#include <vector>

// custom MyVector (An example)
template<typename T> struct MyVector {};

template<typename Test, template<typename...> class ClassType>
struct is_specialization final : std::false_type {};

template<template<typename...> class ClassType, typename... Args>
struct is_specialization<ClassType<Args...>, ClassType> final : std::true_type {};

And the print function could be in c++17:

void print() const /* noexcept */
{
if constexpr (is_specialization<T, std::vector>::value)// when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else if constexpr (is_specialization<T, ::MyVector>::value) // custom `MyVector`
{
std::cout << "MyVector\n";
}
else // for types other than `std::vector<>` and custom `MyVector`
{
std::cout << mData << std::endl;
}
}

(See Live Online)

Template member function specialization of a templated class without specifying the class template parameter

You can use a technique called tag dispatch and replace the template specialisations by function overloads.

template<typename>
struct Tag {};

template <class A>
struct C3
{
void f_impl(Tag<int>) const;
void f_impl(Tag<char>) const;
template<class B>
void f() const {
f_impl(Tag<B>{});
}
};

struct D { static int g(void){ return 999; } };

template <class A>
void C3<A>::f_impl(Tag<int>) const { std::cout<<A::g()+1<<std::endl; }

template <class A>
void C3<A>::f_impl(Tag<char>) const { std::cout<<A::g()+2<<std::endl; }

Then your call site looks exactly as you want:

 C3<D> c3; c3.f<int>();  // expect to see 1000
C3<D> c4; c4.f<char>(); // expect to see 1001

Full example here.

C++ template member function not declared in this scope

The correct way to invoke a member function is obj.function(...). You need:

cout << test.IsEmpty() << endl;   //error points to here

Template function does not work for pointer-to-member-function taking const ref

Your issue is that you have conflict deductions for Args between:

  • R (T::*fun)(Args...)
  • Args... args

I suggest to have more generic code (no duplications between R (T::*fun)(Args...) and

const version R (T::*fun)(Args...) const and other alternative) with:

template<class T, class F, class... Args>
decltype(auto) call_or_throw(const std::weak_ptr<T>& ptr,
const std::string& error,
F f,
Args&&... args)
{
if (auto sp = ptr.lock())
{
return std::invoke(f, *sp, std::forward<Args>(args)...);
}
else
{
throw std::runtime_error(error.c_str());
}
}

How to create a template function within a class? (C++)

Your guess is the correct one. The only thing you have to remember is that the member function template definition (in addition to the declaration) should be in the header file, not the cpp, though it does not have to be in the body of the class declaration itself.

template member function of template class called from template function

Try the following code:

template<class T> void g()
{
A<T> a;
a.template f<3>(); // add `template` keyword here
}

According to C++'03 Standard 14.2/4:

When the name of a member template specialization appears after . or -> in a postfix-expression, or after nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.

Future C++ Standard seems to be still require this keyword according to draft n2857 14.3/4. Some compilers has special mode that allows to compile original code without errors (Comeau compiles it in so called relaxed mode).



Related Topics



Leave a reply



Submit