Compiler infering the template argument

64 Views Asked by At
template<typename T>
class A
{
    public:

    A(T &t)
    : t_(t){}

    T t_;
};


int main()
{
    int value;
    A<decltype(value)> a(value);
    // what I wish for : A a(value); 
    // which does not compile "missing template argument before 'a'"
}

Is there a way in the declaration of A (or somewhere else) to hint the compiler that T should be automatically resolved to the type passed to the constructor ?

(ideally c++11, but happy to hear about less old versions)

3

There are 3 best solutions below

0
On BEST ANSWER

In C++11 you can create a simple make_A function like this:

#include <iostream>

template <typename T>
class A {
public:
    A(T &t) : t_(t) {}

    T t_;
};

template <typename T>
A<T> make_A(T&& t) {
    return A<T>(std::forward<T>(t));
}

int main() {
    int value = 0;
    auto a = make_A(value);

    return 0;
}

Demo

0
On

C++17 does it out of the box (or with the help of deduction guides), previous versions cannot.

1
On

As answered by @Quentin, this is only possible starting in C++17. However, if you're fine with calling a function to create your A objects, the following should do what you want in C++11:

template <class T, class NonRefT = typename std::remove_reference<T>::type>
A<NonRefT> create_A (T && t) {
  return A<NonRefT>(std::forward<T>(t));
}

// Or even closer to your original code:
template <class T>
auto create_A (T && t) -> A<decltype(t)> {
  return A<decltype(t)>(std::forward<T>(t));
}

I used std::remove_reference based on your use of decltype, though you might want to use std::decay instead.

int main () {
  int value = 5;
  auto a = create_A(value);
}

If I remember correctly the example code has an edge-case where it does not work as expected pre-C++17. The compiler will elide the copy/move constructor to create a from the rvalue returned by create_A(). However, it will check during compilation whether A's copy/move constructor (which it won't use) is available/accessible. Starting from C++17 the copy/move elision is done "properly" and no copy/move constructor is necessary for such code. (Also, I might be misremembering and it might be checking for copy/move assignment instead.)