0

I wrote a matrix class template which, amongst other things, includes arithmetic operators which are specialized for certain class template parameters utilizing if constexpr statements. The use of this matrix class within another template class may lead to a plethora of error messages of the form ’symbol’ cannot be used before it is initialized if compiled with the Visual C++ compiler (version 19.00.24245). If the matrix class template is used within another class template and then compiled with g++ 7.4.0 on Linux or with the most recent Apple clang version or if it is used in a non-class template and then compiled with the Visual-C++ compiler, the compilation is successful.

To enable the reproduction of the issue described above, a code segment containing a vector class template with one operator which mimics the structure of the afore mentioned matrix class, the compiler flags as well as the error messages are given below. Commenting out line 160 and switching the comment form line 185 to line 186 results in the compiling case.

#include <iostream>
#include <type_traits>

enum class Layout
{
  standard,
  reverse
};

template< typename    T_,
          std::size_t numEls_,
          Layout      layout_ = Layout::standard >
class Vec
{
public:
  using ValueType = T_;

  static constexpr auto numElements = numEls_;
  static constexpr auto layout      = layout_;

  Vec() = default;

  template< typename... Ts >
  Vec( Ts... vals )
  : els_{}
  {
    static_assert( sizeof...( Ts ) == numEls_ );

    T_ tmp[] = { vals... };

    if constexpr ( layout_ == Layout::standard )
    {
      for ( auto i = decltype( numEls_ ){ 0 }; i < numEls_; ++i )
      {
        els_[ i ] = tmp[ i ];
      }
    }
    else // laylout == Layout::reverse
    {
      auto j = numEls_;

      for ( auto i = decltype( numEls_ ){ 0 }; i < numEls_; ++i )
      {
        els_[ i ] = tmp[ --j ];
      }
    }
  }

private:
  template< typename Rhs >
  void AssignFromRhs( const Rhs& rhs )
  {
    static_assert( std::is_same_v< typename Rhs::ValueType, T_ > );
    static_assert( Rhs::numElements == numEls_ );

    auto tmp = rhs.Data();

    if constexpr ( Rhs::layout == layout_ )
    {
      for ( auto i = decltype( numEls_ ){ 0 }; i < numEls_; ++i )
      {
        els_[ i ] = tmp[ i ];
      }
    }
    else // Rhs::layout != layout_
    {
      auto j = numEls_;

      for ( auto i = decltype( numEls_ ){ 0 }; i < numEls_; ++i )
      {
        els_[ i ] = tmp[ --j ];
      }
    }
  }

public:
  template< typename Rhs >
  Vec( const Rhs& rhs )
  {
    AssignFromRhs( rhs );
  }

  template< typename Rhs >
  Vec& operator=( const Rhs& rhs )
  {
    AssignFromRhs( rhs );

    return *this;
  }

  void Cout() const
  {
    std::cout << "[";

    if constexpr ( layout_ == Layout::standard )
    {
      for ( auto i = decltype( numEls_ ){ 0 }; i < numEls_; ++i )
      {
        std::cout << " " << els_[ i ];
      }
    }
    else // laylout == Layout::reverse
    {
      for ( auto i = decltype( numEls_ ){ numEls_ }; i > 0; )
      {
        std::cout << " " << els_[ --i ];
      }
    }

    std::cout << " ]" << std::endl;
  }

  T_* Data()
  {
    return els_;
  }

  const T_* Data() const
  {
    return els_;
  }

  template< typename Rhs >
  auto operator+( const Rhs& rhs )
  {
    static_assert( std::is_same_v< typename Rhs::ValueType, T_ > );
    static_assert( Rhs::numElements == numEls_ );

    Vec res;

    auto tmp = rhs.Data();

    if constexpr ( Rhs::layout == layout_ )
    {
      for ( auto i = decltype( numEls_ ){ 0 }; i < numEls_; ++i )
      {
        res.els_[ i ] = els_[ i ] + tmp[ i ];
      }
    }
    else // Rhs::layout != layout_
    {
      auto j = numEls_;

      for ( auto i = decltype( numEls_ ){ 0 }; i < numEls_; ++i )
      {
        res.els_[ i ] = els_[ i ] + tmp[ --j ];
      }
    }

    return res;
  }

private:
  T_ els_[ numEls_ ];
};

using VecD3S = Vec< double, 3 >;
using VecD3R = Vec< double, 3, Layout::reverse >;

template< int dummy >
class Foo
{
public:
  void DoSomething()
  {
    v3_ = v1_ + v2_;
  }

  VecD3S v1_, v3_;
  VecD3R v2_;
};

int main( const int argc, const char* argv[] )
{
  VecD3S v1 = { 1., 2., 3. };
  VecD3R v2 = { 11., 12., 13. };

  v1.Cout();
  v2.Cout();

  auto v3 = v1 + v2;

  v3.Cout();

  //Foo foo;
  Foo< 1 > foo;

  foo.v1_ = v1;
  foo.v2_ = v2;

  foo.v1_.Cout();
  foo.v2_.Cout();

  foo.DoSomething();

  foo.v3_.Cout();

  return 0;
}
/JMC /permissive- /GS /W3 /Zc:wchar_t /ZI /Gm- /Od /sdl /Fd"x64\Debug\vc142.pdb" /Zc:inline /fp:precise /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /MDd /std:c++17 /FC /Fa"x64\Debug\" /EHsc /nologo /Fo"x64\Debug\" /Fp"x64\Debug\TplTplAutoTest.pch" /diagnostics:column 
Error   C3536    'i': cannot be used before it is initialized   [... line] 135  
Error   C3536    'tmp': cannot be used before it is initialized [... line] 137  
Error   C2109    subscript requires array or pointer type   [... line] 137  
Error   C3536    'i': cannot be used before it is initialized   [... line] 144  
Error   C3536    'j': cannot be used before it is initialized   [... line] 146  
Error   C2109    subscript requires array or pointer type   [... line] 146

My first question is if there is something wrong with the above code, i.e., that it is not conform to the C++17 standard, or if a compiler flag is missing or if the compilation errors are due to a bug in the Visual-C++ compiler? Furthermore I am asking, how the code can be modified to enable the compilation with the Visual Studio compiler without changing its general structure, e.g., forgoing the use of the if constexpr statements? (The reason for not using template specialization (in the matrix class) is that it would increase the code size significantly due to the need to specialize for several template parameters and to also accept proxy classes as arguments.)

esakaen
  • 43
  • 3

0 Answers0