Search code examples
c++templatesostreamif-constexpr

How to prevent the compiler from checking the syntactic correctness of a certain branch


I basically want to select one of the branches at compile-time but can't figure out how to address the error that shows up.

Here is the code (link):

#include <iostream>
#include <vector>
#include <type_traits>


template <typename charT>
struct Foo
{
    using value_type = charT;

    std::vector<value_type> vec;

    void printVec( std::basic_ostream<charT>& out_stream )
    {
        out_stream.write( std::data( vec ), std::size( vec ) ).write( "\n", 1 );
    }
};

// specialization for char
template <>
void Foo<char>::printVec( std::basic_ostream<char>& out_stream )
{
    out_stream.write( std::data( vec ), std::size( vec ) ).write( "\n", 1 );
}

// specialization for wchar_t
template <>
void Foo<wchar_t>::printVec( std::basic_ostream<wchar_t>& out_stream )
{
    out_stream.write( std::data( vec ), std::size( vec ) ).write( L"\n", 1 );
}

int main( )
{
    using FooChar = Foo<char>;
    using FooWideChar = Foo<wchar_t>;

#define IS_CHAR 1

#if IS_CHAR == 1
    FooChar foo;
    foo.vec.resize( 10, '$' );
#else
    FooWideChar foo;
    foo.vec.resize( 10, L'#' );
#endif

    if constexpr ( std::is_same_v< decltype( foo )::value_type,
                                   decltype( std::cout )::char_type > )
    {
        foo.printVec( std::cout );
    }
    else if constexpr ( std::is_same_v< decltype( foo )::value_type,
                                        decltype( std::wcout )::char_type > )
    {
        foo.printVec( std::wcout ); // this is where the compile error occurs
    }
    else
    {
        static_assert( std::is_same_v< decltype( foo )::value_type,
                                       decltype( std::cout )::char_type > ||
                       std::is_same_v< decltype( foo )::value_type,
                                       decltype( std::wcout )::char_type >,
                       "character type not supported" );
    }
}

The error message:

cannot convert 'std::wostream' {aka 'std::basic_ostream<wchar_t>'} to 'std::basic_ostream<char>&'

The message is self-explanatory however I still don't know how to get around this issue. I guess this problem would not arise if the compiler wouldn't check the syntax of the statement inside the else if branch. Is there any way to make the above snippet compile?


Solution

  • Lift your if constexpr logic into a template function:

    template <typename charT>
    void printIt( Foo<charT>& foo )
    {
        if constexpr ( std::is_same_v<charT, char> )
            foo.printVec( std::cout );
        else if constexpr ( std::is_same_v<charT, wchar_t> )
            foo.printVec( std::wcout );
        else
            static_assert( sizeof(charT) == 0, "character type not supported" );
    }
    

    Also, note you don't need two definitions of printVec() for char. You can simply declare it in the class without a definition:

    void printVec( std::basic_ostream<charT>& out_stream );
    

    It should probably be const, then printIt() can take const Foo&.