Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why is std::conditional reject T&?

I want to return void if template parameter is void, and return T& if template parameter is T. So, I write the next MRE code:

#include<type_traits>

template<class T>
struct Foo {
    using X = std::conditional<std::is_void_v<T>, void, T&>::type;

    X bar(){ }
};

int main(){
    Foo<void> foo;
    return 0;
}

I compile this code with g++-13 --std=c++20 main.cpp or clang++ --std=c++20 main.cpp.
I am getting this error:

main.cpp:5:58: error: cannot form a reference to 'void'
    5 |     using X = std::conditional<std::is_void_v<T>, void, T&>::type;
      |                                                          ^
main.cpp:11:15: note: in instantiation of template class 'Foo<void>' requested here
   11 |     Foo<void> foo1;
      |               ^
1 error generated.

Why? And how to use std::conditional with T&?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

The problem is there is no "template indirection" preventing the compiler from trying to fill in the T in T& and the compiler needs to instantiate all template arguments for void immediately.

You can add a level of indirection by using std::add_lvalue_reference_t: https://godbolt.org/z/cesc9sWT4

using X = std::conditional_t<std::is_void_v<T>, void, std::add_lvalue_reference_t<T>>;

You can also work around this by adding that level of instantiation delay with an explicit helper template which is specialized on void: https://godbolt.org/z/6vxjYG6ez

template<typename T>
struct ReferenceOrVoid
{
    using type = T&;
};
template<>
struct ReferenceOrVoid<void>
{
    using type = void;
};
template<typename T>
using ReferenceOrVoid_t = typename ReferenceOrVoid<T>::type;

using X = ReferenceOrVoid_t<T>;

Unfortunately, that means not using std::conditional_t directly and is a visual indirection on what’s going on. On the other hand, it names the operation you’re doing, so you don’t have to parse the type trait expression to know what it’s doing.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading