C++ C++;在模板类内部使用非模板(但特定于类型)函数

C++ C++;在模板类内部使用非模板(但特定于类型)函数,c++,templates,forward-declaration,C++,Templates,Forward Declaration,我得到的情况是,我有一个通用库,在很多项目中使用。 这个库只包含一个非常简单的模板类(在template_class.h中)。然后,模板类将在多个可执行文件中使用(由main.cpp表示)。 main.cpp还可以访问autogenerated.h中的某些自动生成函数。 顾名思义,autogenerated.h中的函数是自动生成的,不能修改。 这个“scalefft”实现应该在模板函数中使用。 我的意图是保持模板类独立于具体用法 现在的问题是如何使自动生成的函数可以在模板中使用。是否可以选择转发

我得到的情况是,我有一个通用库,在很多项目中使用。 这个库只包含一个非常简单的模板类(在template_class.h中)。然后,模板类将在多个可执行文件中使用(由main.cpp表示)。 main.cpp还可以访问autogenerated.h中的某些自动生成函数。 顾名思义,autogenerated.h中的函数是自动生成的,不能修改。 这个“scalefft”实现应该在模板函数中使用。 我的意图是保持模板类独立于具体用法

现在的问题是如何使自动生成的函数可以在模板中使用。是否可以选择转发声明?还是需要将函数指针作为模板参数传递

这里有一个最小的(不工作)示例: (据我记忆所及,此示例甚至在VC++中工作-但是现在我只访问了gcc 7,它给了我一条错误消息:“'scalefft'未在此范围内声明,并且在实例化点通过依赖参数的查找未找到任何声明”

非常感谢

// template_class.h

#pragma once

template<typename T>
class Aggregator
{
  T sum_ = 0;

 public:

  T aggregate(T value){
    sum_ += scaleFct(value);
    return sum_;
  }
};

在模板类内调用函数时,查找过程大致如下:

  • 我能在我现在所在的班级里看到它吗(不包括家长)
  • 我可以在类型不依赖于模板参数的父级中看到它吗
  • 我可以在声明模板的地方找到它吗
  • 所有这些都发生在中替换模板参数之前。这是在声明时完成的,而不是在实例化时完成的

    传入类型后,将进行另一次查找:

  • 我可以通过在函数的模板参数相关参数上进行参数相关查找来找到它吗
  • 这是在实例化点进行的。注意,如果两个不同的实际实例化点(我认为,包括实例化发生的翻译单元的末尾)导致不同的查找结果,那么您的程序是格式错误的,不需要诊断

    在MSVC的早期版本中,1、2和3被延迟到实例化点,它还研究了类型依赖于模板参数的基类

    每个现代编译器基本上都使用上述步骤(我可能犯了一些小错误,如果您需要确切地知道发生了什么,请进一步研究;但上面应该给出了要点)

    您将看到在声明点,
    scaleFct
    不可见,因此找不到它。并且依赖于参数的查找不会在基元类型上发生,因此步骤4也找不到它

    解决问题的最简单/最好的方法是将“autogenerated.h”包含在
    template\u class.h

    <>另一个不好的选项是<代码>包含“自动生成.h”/<代码>代码> >包含“TeMePiel.Cype .h”< /C>。这很烂;如果C++中包含了特定的顺序,通常不希望头文件工作,这会使代码基础非常脆弱。 另一个选项是将
    template_class.h
    包装在包含自动生成的模板类标题的标题中

    另一个选项是将
    scaleFct
    的前向声明添加到
    template\u class.h
    ,可能是将它们放入
    forward\u declare\u autogenerated.h
    头文件中

    另一个选择是向聚合器添加蹦床

    template<typename T, class Trampoline>
    class Aggregator
    {
      T sum_ = 0;
    
    public:
    
      T aggregate(T value){
        sum_ += Trampoline::scaleFct(value);
        return sum_;
      };
    };
    
    您可以为每个
    T
    专门化它,并将调用注入到自动生成的代码中。您还可以在此处传入显式蹦床

    另一种方法是标记分派

    namespace adl {
      template<class T>
      struct tag_t {};
      template<class T>
      constexpr tag_t<T> tag{};
    }
    
    template<typename T>
    class Aggregator
    {
      T sum_ = 0;
    
    public:
    
      T aggregate(T value){
        sum_ += scaleFct(adl::tag<T>, value);
        return sum_;
      };
    };
    
    名称空间adl{
    模板
    结构标记{};
    模板
    constexpr tag_t tag{};
    }
    模板
    类聚合器
    {
    T和=0;
    公众:
    T骨料(T值){
    sum+=scaleFct(adl::tag,value);
    返回和;
    };
    };
    
    然后加上

    namespace adl {
      inline float scaleFct(tag_t<float>, float f) { return ::scaleFct(f); }
    }
    
    名称空间adl{
    内联浮点scaleFct(tag_t,float f){return::scaleFct(f);}
    }
    

    在执行
    之前,聚合器将通过ADL查找该函数。

    在模板类中调用函数时,查找过程大致如下:

  • 我能在我现在所在的班级里看到它吗(不包括家长)
  • 我可以在类型不依赖于模板参数的父级中看到它吗
  • 我可以在声明模板的地方找到它吗
  • 所有这些都发生在中替换模板参数之前。这是在声明时完成的,而不是在实例化时完成的

    传入类型后,将进行另一次查找:

  • 我可以通过在函数的模板参数相关参数上进行参数相关查找来找到它吗
  • 这是在实例化点进行的。注意,如果两个不同的实际实例化点(我认为,包括实例化发生的翻译单元的末尾)导致不同的查找结果,那么您的程序是格式错误的,不需要诊断

    在MSVC的早期版本中,1、2和3被延迟到实例化点,它还研究了类型依赖于模板参数的基类

    每个现代编译器基本上都使用上述步骤(我可能犯了一些小错误,如果您需要确切地知道发生了什么,请进一步研究;但上面应该给出了要点)

    您将看到在声明点,
    scaleFct
    不可见,因此找不到它。并且依赖于参数的查找不会在基元类型上发生,因此步骤4也找不到它

    解决问题的最简单/最好的方法是将“autogenerated.h”包含在
    template\u class.h

    另一个不太好的选择是在包含temp之前包含“autogenerated.h”
    template<class T>
    struct Traits {
      static T scaleFct(T in){ return ::scaleFct(in); }
    };
    template<typename T, typename Trampoline=Traits<T>>
    class Aggregator
    {
      T sum_ = 0;
    
    public:
    
      T aggregate(T value){
        sum_ += Trampoline::scaleFct(value);
        return sum_;
      };
    };
    
    namespace adl {
      template<class T>
      struct tag_t {};
      template<class T>
      constexpr tag_t<T> tag{};
    }
    
    template<typename T>
    class Aggregator
    {
      T sum_ = 0;
    
    public:
    
      T aggregate(T value){
        sum_ += scaleFct(adl::tag<T>, value);
        return sum_;
      };
    };
    
    namespace adl {
      inline float scaleFct(tag_t<float>, float f) { return ::scaleFct(f); }
    }