Home > front end >  How to conditionally get a `T` or `const T&` from template type `T`?
How to conditionally get a `T` or `const T&` from template type `T`?

Time:04-18

I want to have my template function print(The actual cenario is more complex template function) to have either a const T& or just T(pass by value), depending up on the characteristics of type T.

In other words, if a type T is easily copyable (i.e. such as primitive types int, float, double, std::initializer_list<>, trivially copyable, etc...) and does not make a lot of copying effort, I want to have template instatiation pass by value, otherwise a const l-value reference to the passed type T.

After researching I figured out the folloing two way of doing it. I tried with both std::conditional_t as well as if constexpr function decltypeing as follows:

#include <type_traits>
#include <iostream>
#include <string>
using namespace std::string_literals;

#if 1 // template function way --- (1)

template<typename T>
constexpr auto type_helper() noexcept
{
    if constexpr (std::is_fundamental_v<T>) return T{};
    else return std::add_const_t<std::add_lvalue_reference_t<T>>{};
};

template<typename T> using conditional_copy_ref_t = decltype(type_helper<T>());

#elif 0 // using std::conditional_t  --- (2)
template<typename T>
using conditional_copy_ref_t = std::conditional_t<std::is_fundamental_v<T>, T, const T&>;

#endif


template <typename T>
auto print(conditional_copy_ref_t<T> arg) noexcept 
// could not deduce template ^^^^^^^^^^^^ argument for 'T'
{
    // do something with arg!
    
};

int main()
{
    print(5);        // should be pass by value
    print("string"s);// should be const std::string&
}

Here is the code in online compilers: https://gcc.godbolt.org/z/5fEaE3M5o

However, both the implementations fails due to the fact of non-deduced context template deduction(I have researched agin to understand the error)!

In MSVS I have the error:

error C2672: 'print': no matching overloaded function found
error C2783: 'auto print(unknown-type) noexcept': could not deduce template argument for 'T'

What am I doing wrong? Is it even possible? How to fix this error and get the correct type here?

CodePudding user response:

Because T is in a non-deduced context, there is no way for the compiler to deduce the type of T. Instead, you can use enable_if to help the compiler deduce the type of T

template <typename T>
std::enable_if_t<std::is_fundamental_v<T>>
print(T) noexcept { /* */ }


template <typename T>
std::enable_if_t<!std::is_fundamental_v<T>>
print(const T&) noexcept { /* */ }

Demo

CodePudding user response:

You can tell the compiler what T is explicitly so it doesn't have to deduce it:

#include <type_traits>
#include <iostream>
#include <string>

template<typename T>
using conditional_copy_ref_t = std::conditional_t<std::is_fundamental_v<T>, T, const T&>;

template <typename T>
auto print(conditional_copy_ref_t<T> arg) noexcept 
{
};

int main()
{
    print<int>(5);
    print<std::string>("string");
}

Test this code on Godbolt

  • Related