1

Is it possible (and is it a good idea) to conditionally define methods for some container class (template<typename ThingType> class Container) depending on the type of its elements? I first thought it was possible after reading about std::enable_if, but now I am not certain I understand.

Below is my attempt (click here to run on ideone). In the case that std::is_base_of<ThingBase, ThingType>::value is false, a return type for p will not be defined. I figured the compiler would just instantiate the object of a class without that method. But it turns out it doesn't compile.

Is there another tool for the job? Or should I write two Container-like classes that have different behavior depending on what ThingType is? Or maybe this is a job for a specialization.

#include <iostream>
#include <type_traits>
#include <vector>


class ThingBase {
public:
    virtual void printHi() = 0;
    
};

class Thing : public ThingBase
{
    void printHi(){
        std::cout << "hi\n";
    }   
};


template<typename ThingType>
class Container{
private:
    std::vector<ThingType> m_things;
public:

    typename std::enable_if<std::is_base_of<ThingBase, ThingType>::value>::type p()
    {
        m_things[0].printHi();
    };
};


int main() {
    
    //Container<Thing> stuff; // works!
    Container<int> stuff; // doesn't work :(
    
    return 0;
}

Edits:

The error message from the compiler is

prog.cpp: In instantiation of ‘class Container<int>’:
prog.cpp:36:17:   required from here
prog.cpp:26:78: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
  typename std::enable_if<std::is_base_of<ThingBase, ThingType>::value>::type p()

@StoryTeller - Unslander Monica I don't intend for this method to be overloaded. I want it to be available to the end user whenever it makes sense for it to be available. There will only be one of these p methods, and it should take only one (relatively simple) signature.

Taylor
  • 1,797
  • 4
  • 26
  • 51
  • 1
    It's generally a good idea to also add the compiler error message here. – cigien Feb 27 '21 at 19:09
  • 1
    Is the method you seek to define meant to be overloaded? Will there be more than one `p`? Will this one have a unique signature? – StoryTeller - Unslander Monica Feb 27 '21 at 19:20
  • 1
    Are you limited to C++11? C++20 has `requires` which can conveniently disable unneeded methods. – HolyBlackCat Feb 27 '21 at 19:28
  • @HolyBlackCat that sounds pretty awesome, but I better stick to c++11 – Taylor Feb 27 '21 at 19:34
  • For "how to do the question in title" see [C++ templates: conditionally enabled member function - Stack Overflow](https://stackoverflow.com/questions/26633239/c-templates-conditionally-enabled-member-function) (although as the answer below pointed out, not necessary in this case) – user202729 Dec 17 '21 at 13:17

1 Answers1

4

I don't intend for this method to be overloaded. I want it to be available to the end user whenever it makes sense for it to be available. There will only be one of these p methods, and it should take only one (relatively simple) signature.

This simplifies the exercise quite a bit. The solution is... to do nothing special.

void p() {
    m_things[0].printHi();
}

When a class template is implicitly instantiated, only the declarations of member function are instantiated along with it. Definitions are not instantiated until an attempt is made to use the member.

So you do not need to do anything special. The error will happen if it's used when it cannot be used.

If still you wish to ensure derivability, and produce a descriptive error in that case, you can add a static_assert to the member function body. Simply use the is_base_of test for a condition, and add a nice string to accompany it.

This is a common approach when writing generic utilities. SFINAE's primary purpose on the other hand is to control overload resolution. But you aren't doing it here.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • This works for `int`, but what about a type that happens to have a `printHi` but is not derived from `ThingBase`? At least that's what I was assuming from "anytime it makes sense", and the `is_base_of`. – cigien Feb 27 '21 at 19:46
  • @cigien - *"but what about a type.."* What about it? If the OP doesn't want the extra genericity they can static assert, as I indicated. Either way, the use of SFINAE for case like this verges too much to a cargo cult. – StoryTeller - Unslander Monica Feb 27 '21 at 19:55
  • Oh, right, you did mention the `static_assert`. And yeah, I agree, `enable_if` is overkill for this job. – cigien Feb 27 '21 at 19:57