Search code examples
c++templatesimplicit-conversiondeleted-functions

Use a C++ template to define a function but explicitly delete it to prevent misuse


I have a template class with a constructor in which I want to explicitly forbid the usage of any type beyond a well defined list, like this:

template<typename Anything>
class MyClass
{
public:
    MyClass(int &);
    MyClass(float &);
    MyClass(Anything &) = delete;
}

However, since the code for the integer and the double version are identical, and only differ in the type, I'd like to define it using the templatized version, such as:

template<typename Anything>
MyClass<Anything>::MyClass(Anything &var)
{
    /* Code that operates over var */
    ...
}

instead of virtually having to duplicate the code for both valid constructors.

However, when I tried to do so, I get:

error: redefinition of 'MyClass<Anything>::MyClass(Anything&)'

It works by removing the "= delete".

Is there a way to use the template to define the function but without explicitly allowing to receive more types than the ones also explicitly described?

I checked How can I prevent implicit conversions in a function template? and also Why can I prevent implicit conversions for primitives but not user-defined types?, but their issues don't seem to be equivalent to the current one.

Thanks a lot.

UPDATE: Using gcc-4.8.5, it works!!! Even with the = delete keyword included.


Solution

  • The problem with your definition is that you're trying to implement the exact function that you have marked with = delete.

    You actually want another function template that works with both int and float. You can achieve that by first defining an IntOrFloat predicate:

    template <typename T>
    using IntOrFloat = std::bool_constant<
        std::is_same_v<T, int> || std::is_same_v<T, float>>; 
    

    Then you can define two unambiguous constructors that use std::enable_if_t to check whether or not the predicate is fulfilled by the type passed in by the user:

    class MyClass
    {
    public:
        template <typename T, 
                  std::enable_if_t<IntOrFloat<T>{}>* = nullptr>
        MyClass(T&);
    
        template <typename Anything, 
                  std::enable_if_t<!IntOrFloat<Anything>{}>* = nullptr>
        MyClass(Anything&) = delete;
    };
    

    Usage example:

    int main()
    {
        int v0 = 0;
        float v1 = 0.f;
        const char* v2 = "igijsdg";
    
        MyClass x0{v0}; // OK
        MyClass x1{v1}; // OK
        MyClass x2{v2}; // Error
    }
    

    live example on wandbox