How to avoid code duplicates with class template specializations

Viewed 93

I'd like to avoid the duplicates in the code below.

#include <iostream>

struct Bar{};

template <class... Args>
struct FooClass;

template <class... Args>
inline void foo(Args&&... args) {
  FooClass<Args...>::impl(std::forward<Args>(args)...);
}

// Duplicate 1.
// Const ref version
template <>
struct FooClass<Bar const&> {
  inline static void impl(const Bar& b) {
    std::cout << "dup1" << std::endl;
  }
};

// Duplicate 2.
// Copy version
template <>
struct FooClass<Bar> {
  inline static void impl(const Bar& b) {
    std::cout << "dup2" << std::endl;
  }
};

// Duplicate 3.
// Non-const ref version
template <>
struct FooClass<Bar&> {
  inline static void impl(const Bar& b) {
    std::cout << "dup3" << std::endl;
  }
};

int main()
{
  const Bar b2;
  foo(b2);  
  foo(Bar{});
  Bar b;
  foo(b);
}

I'm pretty sure this is possible using enable_if and universal references somehow, but I could not figure it out.

Btw., this program outputs:

dup1
dup2
dup3

and it won't compile if any of the three specializations are commented out.

2 Answers

Why not just strip off const and & from the template argument?

template<class... Args>
void foo(Args&&... args) {
    FooClass<std::decay_t<Args>...>::impl(std::forward<Args>(args)...);
    //       ^^^^^^^^^^^^
}

template<>
struct FooClass<Bar> {
    static void impl(const Bar&) {
        std::cout << "bar" << std::endl;
    }
};

int main() {
    const Bar b2;
    foo(b2);       // prints "bar"
    foo(Bar{});    // prints "bar"
    Bar b;
    foo(b);        // prints "bar"
}

Demo

Since you're perfectly forwarding your arguments to impl, as far as I know, you're going to need to have all the duplicates, since the compiler has to exactly match the type for specializations.

A simple fix would be to just copy everything:

#include <iostream>

struct Bar{};

template <class... Args>
struct FooClass;

template <class... Args>
inline void foo(Args... args) {
  FooClass<Args...>::impl(args...);
}

// Duplicate 1.
// Const ref version
template <>
struct FooClass<Bar> {
  inline static void impl(const Bar& b) {
    std::cout << "dup1" << std::endl;
  }
};

int main()
{
  const Bar b2;
  foo(b2);  
  foo(Bar{});
  Bar b;
  foo(b);
}

This compiles fine in Clang 12.0.0.

Related