0

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?

digito_evo
  • 3,216
  • 2
  • 14
  • 42

1 Answers1

1

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&.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • So does this add to the runtime cost? Also, can we just put this logic into the `printVec` which is defined inside the class `Foo`? – digito_evo Sep 10 '22 at 08:29
  • 1
    No it does not add to the runtime cost. Yes you can put `std::cout` and `std::wcout` directly into `printVec()`, but I assumed you wanted to be able to pass other streams than the standard streams (such as a file) to `printVec()`. If not, there's no reason for `printIt()` to be separate. – John Zwinck Sep 10 '22 at 08:42
  • Yes, I want that flexibility. Thanks for the info. – digito_evo Sep 10 '22 at 08:49