3

Here's a minimal code example to show what I'm attempting that works, but not how I'd like it to:

#include <string>
#include <type_traits>
#include <iostream>
struct string_tag {
    using R=const std::string;
};

struct int_tag {
    using R=const int;
};

template <bool TS>
class Wibble {
    public:
    template<typename TAG>
    typename TAG::R getValue(TAG);
};

template <bool TS>
template <typename TAG>
typename TAG::R Wibble<TS>::getValue(TAG) {
    if constexpr (std::is_same<TAG, string_tag>::value) {
        return "hello";
    }
    
    if constexpr (std::is_same<TAG, int_tag>::value) {
        return 42;
    }
}

// instantiate classes
template class Wibble<true>;
template class Wibble<false>;

int main () {
    Wibble<true> tw;
    Wibble<false> fw;
    std::cout << "tw string: " << tw.getValue(string_tag{}) << std::endl;
    std::cout << "tw int: " << tw.getValue(int_tag{}) << std::endl;
    std::cout << "fw string: " <<fw.getValue(string_tag{}) << std::endl;
    std::cout << "fw int: " << fw.getValue(int_tag{}) << std::endl;
}

example in godbolt here

The part I want to change is the ugly function template definition with all the constexpr logic. I'd like to be able to define the different specializations in TAG standalone, but doing this gives me redefinition of ... errors.

Syntax like the following would be nice:

template<bool TS>
template<>
string_tag::R Wibble<TS>::getValue(string_tag) {
  return "hello";
}

but computer says "no".

VorpalSword
  • 1,223
  • 2
  • 10
  • 25
  • 1
    It looks like you want [`concepts`](https://en.cppreference.com/w/cpp/concepts). Are you up for C++20 ? Re: _"computer says "no""_ +1 – Ted Lyngmo Jul 07 '22 at 22:14
  • it's probably time I dragged myself kicking & screaming into c++20 :-) Thanks! – VorpalSword Jul 07 '22 at 22:18
  • Don't take my word for it - I'm just shouting out buzzwords :-) I'm pretty much happy at C++17 myself. :-) – Ted Lyngmo Jul 07 '22 at 22:21
  • 1
    Does this answer your question? [explicit specialization of template class member function](https://stackoverflow.com/questions/5512910/explicit-specialization-of-template-class-member-function) – Quimby Jul 07 '22 at 22:21
  • it looks like it could. I'll try it out. thanks! – VorpalSword Jul 07 '22 at 22:26
  • 1
    That other question requires the template to be specialized first, as described, which isn't the case here. Given that `getValue()` does not depend on the `TS` parameter, it should be implemented as a template function separately, that can be specialized normally. The class template can still define its own `getValue()` method that does nothing but call the "real" `getvalue`. I expect modern C++ compilers to optimize away the extra function call. – Sam Varshavchik Jul 07 '22 at 22:29
  • Why do you need template method instead of just overloading it on the needed type? – alagner Jul 07 '22 at 22:45
  • I want different return types - that's what the tag dispatching gets me. – VorpalSword Jul 07 '22 at 23:07
  • 1
    @VorpalSword You can get different return types via overloading so long as you're still using the tag as a function argument. [demo](https://godbolt.org/z/K5KK3WMq9). Does this achieve what you're looking for? – cigien Jul 08 '22 at 03:49
  • that'd work but I prefer @alanger's answer. Thanks all the same though :-) – VorpalSword Jul 08 '22 at 17:35

1 Answers1

5

I gave it a thought, read language specs etc. and the following things come to my mind:

  1. Class template has to be specialized in order to specialize member function template. Period. This cannot be overcome with concepts, or at least I haven't found a way. I guess you don't want to replicate the code for each case of TS. Maybe it can be done automagically with some Alexandrescu-style metaprogramming techniques, but I can't think of anything right off the bat.

  2. Overloads instead of templates are a good alternative but I'm guessing you'd like to be able to add them out-of-line, instead of inside the class...

  3. Then I recalled David Wheeler: “All problems in computer science can be solved by another level of indirection." So let's add one:

namespace detail
{
template<typename TAG> auto getValue(TAG);

template<>
auto getValue<string_tag>(string_tag)
{
    return "hi";
}


template<>
auto getValue<int_tag>(int_tag)
{
    return 42;
}

template<>
auto getValue<double_tag>(double_tag)
{
    return 1324.2;
}

}

template<bool TS>
template<typename TAG>
auto Wibble<TS>::getValue(TAG t)
{
    return detail::getValue(t);
}

https://godbolt.org/z/GsPK4MP8M Hope it helps.

alagner
  • 3,448
  • 1
  • 13
  • 25
  • thanks so much! this worked perfectly. A rare example of winning the "be reasonable and do it my way!" interaction with compilers ;-) – VorpalSword Jul 08 '22 at 17:32