0

Problem

This is hands down the strangest thing I've ever experienced. After 3 hours I managed to boil it down to this reproducible example (code below, sorry if it's a little messy, I tried my best)

I marked the line with a comment. Please not, that the aline above is not a problem. I also provide some other definitions for the Test struct. If I replace the one provided in the MWE, then it compiles flawlessly. Most notably, I can just assign weights[0] to A and then call A.applyFunction<float>(...). So what is it with g++, that it doesn't like my array element? I first thought, that it might be due to the templated size, and indeed, if I replace that with a fixed size, it compiles. However, then A=weights[0] shouldn't work either. I'm really curious, what the explanation will be.

Error message

g++ -o test -c test.cpp -O0 -ggdb -DDEBUG -D__DEBUG -Wextra -Wshadow -Wconversion -Werror -Wall  
test.cpp: In constructor ‘Test<sz>::Test()’:  
test.cpp:79:43: error: expected primary-expression before ‘float’  
   79 |     weights[0] = weights[0].applyFunction<float>(test);  
      |                                           ^~~~~  
test.cpp:79:43: error: expected ‘;’ before ‘float’
   79 |     weights[0] = weights[0].applyFunction<float>(test); 

Compiler Info:

g++ --version g++ (Ubuntu 9-20190428-1ubuntu1~18.04.york0) 9.0.1 20190428 (prerelease) [gcc-9-branch revision 270630] Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Code

#include <algorithm>
#include <array>
#include <cstdio>
#include <functional>
#include <iostream>
#include <iterator>
#include <ostream>
#include <stdexcept>

namespace Math{
  template<typename R>
  struct Matrix{
  private: unsigned short nRows, nCols;
  public:
    R* data;
    Matrix<R>() : nRows(0), nCols(0){}
    Matrix<R>(unsigned short nRows_, unsigned short nCols_) : Matrix<R>(nRows_, nCols_, R{}){
    }
    Matrix<R>(unsigned short nRows_, unsigned short nCols_, const R& val) : nRows(nRows_),nCols(nCols_){
      data = new R[nRows * nCols];
      for( unsigned short i = 0; i < nRows; ++i ){
        for( unsigned short j = 0; j < nCols; ++j ){
          data[i*nCols+j] = val;
        }
      }
    }
    Matrix<R>(unsigned short nCols_, unsigned short nRows_, R* vals) : nRows(nRows_), nCols(nCols_){
      data = new R[nRows * nCols];
      std::copy(vals, vals+nRows*nCols, data);
    }
    ~Matrix<R>(){
      delete[] data;
    }
    Matrix<R>(const Matrix<R>& m) : nRows(m.nRows), nCols(m.nCols){
      data = new R[nRows * nCols];
      data = m.data;
    }
    Matrix<R>& operator=(const Matrix<R>& m){
        nRows = m.getNumRows();
        nCols = m.getNumCols();
        data = new R[nRows * nCols];
        std::copy(m.data, m.data+nRows*nCols, data);
        return *this;
    }

    template<typename S>
    Matrix<S> applyFunction(std::function<S(R)> f){
      Matrix<S> result(nCols, nRows);
      for( unsigned short i = 0; i < nRows; ++i ){
        for( unsigned short j = 0; j < nCols; ++j ){
          result.data[i*nCols+j] = f(data[i*nCols+j]);
        }
      }
      return result;
    }
    unsigned short getNumCols() const{
      return nCols;
    }
    unsigned short getNumRows() const{
      return nRows;
    }
  };
}

float test(float x){return x*x;}

using Math::Matrix;

template<unsigned short sz>
struct Test{
  std::array<Matrix<float>, sz+1> weights;

  Test(){

    Matrix<float> A;
    A = Matrix<float>(2, 2, 0.132f);
    weights[0] = Matrix<float>(2, 2, 0.132f);
    A = A.applyFunction<float>(test);
    weights[0] = weights[0].applyFunction<float>(test); /* Line 79 */
  }
};

int main(){
  Test<2> t;
}

Test #2 (works)

template<unsigned short sz>
struct Test{
  std::array<Matrix<float>, sz+1> weights;

  Test(){

    Matrix<float> A;
    A = Matrix<float>(2, 2, 0.132f);
    weights[0] = Matrix<float>(2, 2, 0.132f);
    A = weights[0];
    A = A.applyFunction<float>(test);
//    weights[0] = weights[0].applyFunction<float>(test);
  }
};

Test #3 (works)

template<unsigned short sz>
struct Test{
  std::array<Matrix<float>, 3> weights;

  Test(){

    Matrix<float> A;
    A = Matrix<float>(2, 2, 0.132f);
    weights[0] = Matrix<float>(2, 2, 0.132f);
    A = A.applyFunction<float>(test);
    weights[0] = weights[0].applyFunction<float>(test);
  }
};
infinitezero
  • 1,610
  • 3
  • 14
  • 29
  • 2
    TL;DR of the dupe: Here you need to tell the compiler you want to call a template function since `weights` is a dependent name. That means you need: `weights[0] = weights[0].template applyFunction(test);` – NathanOliver Oct 21 '19 at 20:16
  • Well thanks, that certainly fixes it. However, I'm not quite sure, how this is a dependent name. Yes, the array is templated, but that is just the size, that does not affect the type of `weights`, which is clearly of type `Matrix`, just as `A` is. – infinitezero Oct 21 '19 at 20:50
  • 1
    *Yes, the array is templated, but that is just the size, that does not affect the type of weights* It really does. There could exist a specialization for the template value that changes what it does. Since the compiler can't know this until it instantiates the template (it hasn't done so at this point, it is just doing syntactic parsing) an members of `weights` are dependent on what `sz` is. – NathanOliver Oct 21 '19 at 20:54
  • Just to be clear: In an `std::array` it's not possible, after all `Matrix` has no access to the size. However, if I'd written another class/struct, that takes a size parameter (for lack of the correct terminology), then the `[]`-operator could return an arbitrary type, dependend on the size? – infinitezero Oct 21 '19 at 20:59
  • 1
    Yes. `std::array` is not going to do this, but another template could. Since C++ has to work in the same in all cases, that means `sz` makes it dependent and the compiler can't know what `operator[]` will return until it actually instantiates it. – NathanOliver Oct 21 '19 at 21:01
  • Okay, now I understood, thank you very much! – infinitezero Oct 21 '19 at 21:03

0 Answers0