patterncppMinor
C++ template Partial Specialization Member Function; Am I doing right?
Viewed 0 times
templatefunctionspecializationdoingmemberpartialright
Problem
I'm trying to improve my C++ template metaprogramming skills. From my understanding, I can't partially specialize a function(either member function or not). So, I need to define another class that can be used as template class, which enables us partially specialize template argument.
So, In order to check my understanding, I've wrote a simple code. It uses general template class and partially specialized template class to deal with various types. (I've tested this code using gcc 4.8 / clang 3.4 / MSVC 2015) It would give a result as follows:
Generic
Specialization for std::string
Specialization for bool
Specialization for integral type
Am I doing write using class templates? I need a review for this. Any comment would be helpful.
So, In order to check my understanding, I've wrote a simple code. It uses general template class and partially specialized template class to deal with various types. (I've tested this code using gcc 4.8 / clang 3.4 / MSVC 2015) It would give a result as follows:
Generic
Specialization for std::string
Specialization for bool
Specialization for integral type
Am I doing write using class templates? I need a review for this. Any comment would be helpful.
// a.hpp
#ifndef A_HPP
#define A_HPP
#include
#include
#include
// declaration & implementation for bar_impl::bar
template
struct bar_impl
{
static void bar(const T& input)
{
(void)input;
std::cout
struct bar_impl
{
static void bar(const T& input)
{
(void)input;
std::cout
struct bar_impl
{
static void bar(const T& input)
{
(void)input;
std::cout
struct bar_impl
{
static void bar(const T& input)
{
(void)input;
std::cout
void bar(const T& input);
};
template
void Foo::bar(const T& input)
{
bar_impl::type,
typename std::is_integral::type,
typename std::is_same::type
>::bar(input);
}
#endif // A_HPP
// b.cpp
#include "a.hpp"
int main()
{
Foo foo;
foo.bar(Foo());
foo.bar(std::string());
foo.bar(bool());
foo.bar(int());
return 0;
}
Solution
What you've written does work, and you seem to understand all the concepts (e.g., "you can't partially specialize function templates"), and your coding style (indentation and whatnot) seems fine.
Well, one coding-style improvement: You need those
However, your code doesn't really match any common TMP patterns — which is probably just because you're noodling around, learning, instead of trying to solve a specific problem.
If you just wanted to produce the same output with less code, you would do this:
That is, your code is equivalent to providing two overloads of
Alternatively, you could use tag dispatch, as follows. (But I don't recommend it in this case because it turns out to be surprisingly subtle. Replace the second instance of
Maybe the best approach would be to use two non-interacting levels of tag dispatch: first, is
Well, one coding-style improvement: You need those
(void)input casts to shut up compiler warnings about unused parameters... in C. But in C++ you don't need those casts; what you do instead is, if you're not going to use a parameter, just don't give it a name.static void bar(const T&) { ... }However, your code doesn't really match any common TMP patterns — which is probably just because you're noodling around, learning, instead of trying to solve a specific problem.
If you just wanted to produce the same output with less code, you would do this:
struct Foo
{
template auto bar(const T&) -> std::enable_if_t::value>
{
std::cout auto bar(const T&) -> std::enable_if_t::value>
{
std::cout << "Specialization for integral type" << std::endl;
}
void bar(bool)
{
std::cout << "Specialization for bool" << std::endl;
}
void bar(const std::string&)
{
std::cout << "Specialization for std::string" << std::endl;
}
};
int main()
{
Foo foo;
foo.bar(Foo());
foo.bar(std::string());
foo.bar(bool());
foo.bar(int());
return 0;
}That is, your code is equivalent to providing two overloads of
foo.bar, plus a couple of mutually exclusive templates (one for integral types and one for other types). The specific overloads for bool and std::string will take precedence over the templates.Alternatively, you could use tag dispatch, as follows. (But I don't recommend it in this case because it turns out to be surprisingly subtle. Replace the second instance of
std::true_type with ... and watch that overload silently drop out. I'm not actually sure why.)struct Foo
{
template void bar_impl(const T&, ...) {
std::cout void bar_impl(const T&, std::true_type) {
std::cout void bar(const T& input)
{
bar_impl(input, std::is_integral{});
}
};Maybe the best approach would be to use two non-interacting levels of tag dispatch: first, is
T integral? and second, what is T exactly?template struct BarImpl {
template static void bar(const T&) {
std::cout struct BarImpl {
template static void bar(const T&) {
std::cout void bar(const T& input) {
BarImpl::value>::bar(input);
}
};Code Snippets
static void bar(const T&) { ... }struct Foo
{
template<class T> auto bar(const T&) -> std::enable_if_t<!std::is_integral<T>::value>
{
std::cout << "Generic" << std::endl;
}
template<class T> auto bar(const T&) -> std::enable_if_t<std::is_integral<T>::value>
{
std::cout << "Specialization for integral type" << std::endl;
}
void bar(bool)
{
std::cout << "Specialization for bool" << std::endl;
}
void bar(const std::string&)
{
std::cout << "Specialization for std::string" << std::endl;
}
};
int main()
{
Foo foo;
foo.bar(Foo());
foo.bar(std::string());
foo.bar(bool());
foo.bar(int());
return 0;
}struct Foo
{
template<class T> void bar_impl(const T&, ...) {
std::cout << "Generic" << std::endl;
}
template<class T> void bar_impl(const T&, std::true_type) {
std::cout << "Specialization for integral type" << std::endl;
}
void bar_impl(const std::string&, ...) {
std::cout << "Specialization for std::string" << std::endl;
}
void bar_impl(const bool&, std::true_type) {
std::cout << "Specialization for bool" << std::endl;
}
template<class T> void bar(const T& input)
{
bar_impl(input, std::is_integral<T>{});
}
};template<bool IsIntegral> struct BarImpl {
template<class T> static void bar(const T&) {
std::cout << "Generic" << std::endl;
}
static void bar(const std::string&) {
std::cout << "Specialization for std::string" << std::endl;
}
};
template<> struct BarImpl<true> {
template<class T> static void bar(const T&) {
std::cout << "Specialization for integral type" << std::endl;
}
static void bar(bool) {
std::cout << "Specialization for bool" << std::endl;
}
};
struct Foo {
template<class T> void bar(const T& input) {
BarImpl<std::is_integral<T>::value>::bar(input);
}
};Context
StackExchange Code Review Q#122936, answer score: 6
Revisions (0)
No revisions yet.