How do I prevent my templated constructor to take the class as parameter when passing an object to a function

Viewed 98

I have a class that has a templated constructor that initializes an array, but when I pass my initialized object to an function, the function uses the templated constructor to make a copy(thats what I think).

Is there a correct syntax/way to prevent this from happening?

My code that defines the class, function and the main run code:

#include <stdio.h>
#include <array>

template<typename T, typename Args>
class myClass{

public:
    std::array<T, 10> data;

    template<typename ... Element>
    constexpr myClass(Element&&... input) : data{input...} {};

    myClass(const myClass& obj){
        data = obj.data;
    }
};

template<typename T, typename Args>
constexpr auto myFunction(myClass<T, Args> obj){
    return 0;
}

template<typename T, typename Args>
constexpr auto myFunction(myClass<T, Args>&& obj){
    return 0;
}

int main()
{
    myClass<double, std::tuple<int, int, double>> expr_obj;

    auto temp = expr_obj;//ERROR

    auto result = myFunction(expr_obj); //ERROR

    return 0;
}

The error message looks as follows:

main.cpp: In instantiation of ‘constexpr myClass<T, Args>::myClass(Element&& ...) [with Element = {myClass<double, std::tuple<int, int, double> >&}; T = double; Args = std::tuple<int, int, double>]’:
<span class="error_line" onclick="ide.gotoLine('main.cpp',43)">main.cpp:43:38</span>:   required from here
main.cpp:19:58: error: cannot convert ‘myClass >’ to ‘double’ in initialization
     constexpr myClass(Element&&... input) : data{input...} {};
                                                          ^

I added the error message from CLion since its provides more information:

In instantiation of ‘constexpr myClass<T, Args>::myClass(Element&& ...) [with Element = {myClass<double, std::tuple<int, int, double> >&}; T = double; Args = std::tuple<int, int, double>]’:

cannot convert ‘myClass<double, std::tuple<int, int, double> >’ to ‘double’ in initialization
  304 |     constexpr myClass(Element&&... input) : data{input...} {};
2 Answers

One way to do this is constrain the elements of Element to be of type T. That would look like

template<typename T, typename Args>
class myClass{

public:
    std::array<T, 10> data;

    template<typename ... Element, std::enable_if_t<(std::is_same_v<std::decay_t<Element>, T> && ...), bool> = true>
    //                             ^  this says only enable this template if all Element are T                ^
    constexpr myClass(Element&&... input) : data{input...} {};

    myClass(const myClass& obj){
        data = obj.data;
    }
};

Just mark your templated constructor as explicit:

template <typename T, typename Args>
class myClass {
public:
    std::array<T, 10> data;

    template <typename... Element>
    explicit constexpr myClass(Element&&... input) : data{input...} {};

    myClass(const myClass&) = default;
};
Related