非类型参数模板函数,错误:必须调用对非静态成员函数的引用

问题描述

我正在尝试使用非类型参数模板函数作为类的成员,但遇到了错误。下面是一个最小的工作示例

#include <iostream>

enum Mode {ka,kb,kab};

class Foo {
  public:
    Foo(const double& x = 1.0,const Mode& y = ka) : x_(x),y_(y) {;}

    Mode get() const{
      return y_;
    }
    template<Mode mode> double FooFunc();

  private:
    double x_;
    Mode y_;
};

template<> double Foo::FooFunc<ka>() {
  return 1/x_;
}

template<> double Foo::FooFunc<kb>() {
  return 1/x_/x_;
}

template<> double Foo::FooFunc<kab>() {
  return 1/x_/x_/x_;
}

int main() {
  Foo foo1(2,ka),foo2(2,kb),foo3(2,kab);

  std::cout << "foo1 function result: " << foo1.FooFunc<foo1.get()> << "\n";
  std::cout << "foo2 function result: " << foo2.FooFunc<foo2.get()> << "\n";                                                                                                                                                                                                  
  std::cout << "foo3 function result: " << foo3.FooFunc<foo3.get()> << "\n";

  return 0;
}

我正在用 clang 编译它,如下c++ foo.cpp -o foo -O3 -std=c++11。这会导致以下错误,我不确定是什么问题。任何帮助将不胜感激。

foo.cpp:34:48: error: reference to non-static member function must be called
        std::cout << "foo1 function result: " << foo1.FooFunc<foo1.get()> << "\n";
                                                 ~~~~~^~~~~~~~~~~~~~~~~~~
foo.cpp:12:30: note: possible target for call
                template<Mode mode> double FooFunc();
                                           ^
foo.cpp:35:48: error: reference to non-static member function must be called
        std::cout << "foo2 function result: " << foo2.FooFunc<foo2.get()> << "\n";
                                                 ~~~~~^~~~~~~~~~~~~~~~~~~
foo.cpp:12:30: note: possible target for call
                template<Mode mode> double FooFunc();
                                           ^
foo.cpp:36:48: error: reference to non-static member function must be called
        std::cout << "foo3 function result: " << foo3.FooFunc<foo3.get()> << "\n";
                                                 ~~~~~^~~~~~~~~~~~~~~~~~~
foo.cpp:12:30: note: possible target for call
                template<Mode mode> double FooFunc();
                                           ^
3 errors generated.

解决方法

您的代码的问题在于模板参数必须是编译时常量。对于函数调用,这意味着它必须是 constexpr,而对于类方法,这意味着它必须是 static

你只有两个选择:

  • 要么将 Mode 的配置留给运行时,并允许用户潜在地重新配置 Mode。为此,您必须在函数内引入 if/elseswitch 语句

    class Foo {
      public:
        constexpr Foo(double const x = 1.0,Mode const y = Mode::ka) noexcept
          : x_{x},y_{y} {
          return;
        }
        Mode get() const noexcept {
          return y_;
        }
        double FooFunc() const {
          switch (y_) {
            case Mode::ka: 
              return 1.0/x_;
            case Mode::kb:
              return 1.0/x_/x_;
            case Mode::kab:
              return 1.0/x_/x_/x_;
            default:
              // Throw error
          }
        }
      private:
        double x_;
        Mode y_;
    };
    
  • 如果您不打算让用户随时更改 Mode,您可以将其设为类模板并专门化相应的功能。这样,虽然无法即时更改模式。

    template <Mode M = Mode::ka>
    class Foo {
      public:
        constexpr Foo(double const x = 1.0) noexcept
          : x_(x) {
          return;
        }
        static constexpr Mode get() noexcept {
          return M;
        }
        double FooFunc() const noexcept;
      private:
        double x_;
        Mode y_;
    };
    
    template<>
    inline double Foo<Mode::ka>::FooFunc() const noexcept {
      return 1.0/x_;
    }
    template<>
    inline double Foo<Mode::kb>::FooFunc() const noexcept {
      return 1.0/x_/x_;
    }
    template<>
    inline double Foo<Mode::kab>::FooFunc() const noexcept {
      return 1.0/x_/x_/x_;
    }
    
,

使用 constexpr,您可以:

enum Mode {ka,kb,kab};

class Foo {
public:
    constexpr Foo(const double& x = 1.0,const Mode& y = ka) : x_(x),y_(y) {}

    constexpr Mode get() const{
      return y_;
    }
    template<Mode mode> constexpr double FooFunc() const;

private:
    double x_;
    Mode y_;
};

template<> constexpr double Foo::FooFunc<ka>() const {
  return 1/x_;
}

template<> constexpr double Foo::FooFunc<kb>() const {
  return 1/x_/x_;
}

template<> constexpr double Foo::FooFunc<kab>() const {
  return 1/x_/x_/x_;
}

int main() {
  constexpr Foo foo1(2,ka),foo2(2,kb),foo3(2,kab);

  std::cout << "foo1 function result: " << foo1.FooFunc<foo1.get()>() << "\n";
  std::cout << "foo2 function result: " << foo2.FooFunc<foo2.get()>() << "\n";
  std::cout << "foo3 function result: " << foo3.FooFunc<foo3.get()>() << "\n";
}

Demo