【问题标题】:Is it possible to call a class method and a global method based on the parameters of a class template?是否可以根据类模板的参数调用类方法和全局方法?
【发布时间】:2019-10-25 20:39:39
【问题描述】:

这是我的问题:

#include <string>



struct Print
{
    template <typename T>
    void Printer( const T& data )
    {
        PrinterInstance( data );
    }


    void PrinterInstance( const int& data )
    {
        printf( "INTEGER\n" );
    }
};



void PrinterInstance( const std::string& data )
{
    printf( "STRING\n" );
}



int main()
{
    Print print;
    print.Printer( "3" );
    return 0;
}

在 Print 类中,我有一个模板 Printer,它基本上根据模板的参数调用 PrinterInstance。此外,我应该能够通过在类之外添加更多 PrinterInstance 来扩展此功能。

但这不会编译。如果我只在类中实现 PrinterInstance,那没关系。如果我只在类外实现 PrinterInstance,也可以。但是一旦我在类内和类外都有一个 PrinterInstance,模板将只尝试使用类一。

我可以做这个吗?

编辑: 它必须在 C++11 中工作。

【问题讨论】:

  • 我猜你必须为此使用 SFINAE,因为没有办法将类方法和全局函数视为重载。但在此之前,我建议考虑是否真的需要这样做。为什么在全局范围内需要一些重载,而在类范围内需要一些重载?
  • @scohe001 嗯...不,感谢您的回答,但这根本不是我要问的。 =)
  • @Yksisarvinen 自定义编码方法。例如,想象一个序列化为 json 的类。在类中,我有一个模板化的 operator=(),它调用 to_json(int)、to_json(double) 和所有基本数据类型......但是用户应该能够创建自己的 to_json(struct User1)。
  • Print中的方法一定要叫PrinterInstance吗?或者它只需要被称为 PrinterInstance 的东西替换吗? (我怀疑你过度限制了你的解决方案空间)

标签: c++ c++11 templates


【解决方案1】:

如果您可以在Print 内添加模板PrinterInstance()

template <typename T>
void PrinterInstance (T const & data)
 { ::PrinterInstance(data); }

调用全局PrinterInstance(),当类型完全匹配时,编译器使用方法PrinterInstance(),否则使用全局版本(通过模板方法)。

但是,正如 LeDYoM 的回答一样,这要求在 Printer 正文之前定义(或至少声明)PrinterInstance() 的全局版本。

以下是完整的编译示例

#include <string>
#include <iostream>

void PrinterInstance ( const std::string& data )
 { std::cout << "STRING, " << data << std::endl; }

struct Print
 {
   template <typename T>
   void Printer (T const & data)
    { PrinterInstance( data ); }

   template <typename T>
   void PrinterInstance (T const & data)
    { ::PrinterInstance(data); }

   void PrinterInstance (int const & data)
    { std::cout << "INTEGER, " << data << std::endl; }
 };

int main ()
 {
   Print p;
   p.Printer("3");
   p.Printer(5);
 }

【讨论】:

  • 说的清楚,简洁,解决问题!谢谢! =)
  • @wagner 将其限制为全局命名空间中的函数;如果有问题的类,则应在命名空间中进行自定义。
【解决方案2】:

如果你可以使用 C++17:

#include <string>
#include <type_traits>

void PrinterInstance( const std::string& data );

struct Print
{
    template <typename T>
    void Printer( const T& data )
    {
        if constexpr (std::is_same_v<T,int>)
        {
            PrinterInstance( data );
        }
        else
        {
            ::PrinterInstance(data);
        }
    }

    void PrinterInstance( const int& data )
    {
        printf( "INTEGER\n" );
    }
};

void PrinterInstance( const std::string& data )
{
    printf( "STRING\n" );
}

int main()
{
    Print print;
    print.Printer( "3" );
    return 0;
}

注意: 必须首先声明免费的 PrintInstance。 (或者你想要的所有重载)。 您可以根据需要向 if constexpr 添加任意数量的类型(结构内的重载)。 您可能可以将其翻译成 C++14 和一些 SFINAE。

代码:https://godbolt.org/z/S1ZJ93

【讨论】:

  • 不错!但我必须在 C++11 中这样做......我将把它添加到原始问题中。
  • 然后,请注意您没有重载任何内容,因为这两个方法中的函数范围(以及参数的数量,该方法具有隐含的 this)。我会建议所有的都在同一个范围内,所以所有的方法或所有的成员函数。
  • 您确实是完全正确的。我想我会考虑将所有成员函数更改为同一命名空间中的方法,即使使用@max66 解决方案也是如此。
【解决方案3】:

问题是成员在命名空间范围内隐藏了函数。

一种解决方案是分派到正确的函数,例如:

void PrinterInstance( const std::string& data )
{
    printf( "STRING\n" );
}

// Declarations of PrinterInstance for built-in should also be visible before `Printer`

struct Print
{
    template <typename T>
    void Printer( const T& data )
    {
        if constexpr (std::is_invocable<decltype(&Print::PrinterInstance), Print, T>::value) {
            PrinterInstance( data );
        } else {
            using ::PrinterInstance; // make function at global scope visible,
                                     // hide member function and so allows ADL
            PrinterInstance( data );
        }
    }


    void PrinterInstance( const int& data )
    {
        printf( "INTEGER\n" );
    }
};

namespace N
{
    struct S{};

    void PrinterInstance( const N::S& )
    {
        printf( "S\n" );
    }
}

Demo

在 C++11 中,可能是;

struct Print
{
    template <typename T>
    void Printer( const T& data );

    void PrinterInstance( char data )
    {
        printf( "CHAR\n" );
    }

    void PrinterInstance( const int& data )
    {
        printf( "INTEGER\n" );
    }
};

template <typename T>
auto printerImpl(Print& p, const T& arg) -> decltype(p.PrinterInstance(arg))
{
    return p.PrinterInstance(arg);
}

template <typename T>
auto printerImpl(Print&, const T& arg) -> decltype(PrinterInstance(arg))
{
    return PrinterInstance(arg);
}

template <typename T>
void Print::Printer( const T& data )
{
    printerImpl(*this, data);
}

Demo

【讨论】:

  • 对不起,我忘了补充一点,我需要这个才能在 C++11 中工作。感谢您提供非常清晰的示例!
  • std::is_invocable 或等效项可在 C++11 中实现。
  • 使用全局命名空间限定似乎不好。
  • @WagnerVolanin:添加了 C++11 版本。
  • @Yakk-AdamNevraumont:在不创建其他功能的情况下有任何替代方案吗?
猜你喜欢
  • 1970-01-01
  • 2015-02-25
  • 1970-01-01
  • 2019-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-08
相关资源
最近更新 更多