【问题标题】:Overload a C++ function according to the return value根据返回值重载一个C++函数
【发布时间】:2010-09-18 13:49:02
【问题描述】:

我们都知道可以根据参数重载一个函数:

int mul(int i, int j) { return i*j; }
std::string mul(char c, int n) { return std::string(n, c); } 

你能根据返回值重载一个函数吗?定义一个函数,根据返回值的使用方式返回不同的东西:

int n = mul(6, 3); // n = 18
std::string s = mul(6, 3); // s = "666"
// Note that both invocations take the exact same parameters (same types)

您可以假设第一个参数在0-9之间,无需验证输入或进行任何错误处理。

【问题讨论】:

  • 嗯,以下code project article 似乎可以满足您的需求。一定是魔法;)
  • 我希望这是可能的。我曾遇到过几个本可以使用它来简化代码的案例……
  • @Brian - 效果是可能的。请参阅下面的答案。
  • 但是仅仅因为你可以......并不意味着你应该。 :) 很高兴给某人带来困难......
  • 这里只发生了隐式转换,没有发生重载

标签: c++ function puzzle overloading


【解决方案1】:

你必须告诉编译器使用哪个版本。在 C++ 中,您可以通过三种方式实现。

通过键入明确区分调用

您有点作弊,因为您向等待字符的函数发送了一个整数,并且当 '6' 的字符值不是 6 而是 54(ASCII 格式)时错误地发送了数字 6:

std::string mul(char c, int n) { return std::string(n, c); }

std::string s = mul(6, 3); // s = "666"

当然,正确的解决方案是

std::string s = mul(static_cast<char>(54), 3); // s = "666"

我想这值得一提,即使您不想要解决方案。

通过虚拟指针显式区分调用

您可以为每个函数添加一个虚拟参数,从而强制编译器选择正确的函数。最简单的方法是发送返回所需类型的 NULL 虚拟指针:

int mul(int *, int i, int j) { return i*j; }
std::string mul(std::string *, char c, int n) { return std::string(n, c); }

可以和代码一起使用:

int n = mul((int *) NULL, 6, 3); // n = 18
std::string s = mul((std::string *) NULL, 54, 3); // s = "666"

通过模板化返回值来明确区分调用

通过这个解决方案,我们创建了一个“虚拟”函数,其中包含在实例化时不会编译的代码:

template<typename T>
T mul(int i, int j)
{
   // If you get a compile error, it's because you did not use
   // one of the authorized template specializations
   const int k = 25 ; k = 36 ;
}

你会注意到这个函数不会编译,这是一件好事,因为我们只想通过模板特化使用一些有限的函数:

template<>
int mul<int>(int i, int j)
{
   return i * j ;
}

template<>
std::string mul<std::string>(int i, int j)
{
   return std::string(j, static_cast<char>(i)) ;
}

因此,以下代码将编译:

int n = mul<int>(6, 3); // n = 18
std::string s = mul<std::string>(54, 3); // s = "666"

但这个不会:

short n2 = mul<short>(6, 3); // error: assignment of read-only variable ‘k’

通过模板化返回值来明确区分调用,2

嘿,你也作弊了!

是的,我确实为两个“重载”函数使用了相同的参数。但你确实开始作弊了(见上文)......

^_^

更严重的是,如果你需要不同的参数,那么你将编写更多的代码,然后在调用函数时必须显式使用正确的类型以避免歧义:

// For "int, int" calls
template<typename T>
T mul(int i, int j)
{
   // If you get a compile error, it's because you did not use
   // one of the authorized template specializations
   const int k = 25 ; k = 36 ;
}

template<>
int mul<int>(int i, int j)
{
   return i * j ;
}

// For "char, int" calls
template<typename T>
T mul(char i, int j)
{
   // If you get a compile error, it's because you did not use
   // one of the authorized template specializations
   const int k = 25 ; k = 36 ;
}

template<>
std::string mul<std::string>(char i, int j)
{
   return std::string(j, (char) i) ;
}

这段代码会这样使用:

int n = mul<int>(6, 3); // n = 18
std::string s = mul<std::string>('6', 3); // s = "666"

还有下面一行:

short n2 = mul<short>(6, 3); // n = 18

仍然无法编译。

结论

我喜欢 C++...

:-p

【讨论】:

  • 我很伤心。从好的方面来说,它既聪明又准确。不利的一面是,它是一把上膛的枪。
  • @jrodman : 请您详细说明一下“上膛枪”的技术输入,好吗?我看不出我在这个答案中写的任何内容(虚拟指针或模板化返回值)是如何反对语言的。
  • @jrodman:C++ 本身就是一把上膛的枪。这就是我们如此热爱它的原因:)
【解决方案2】:
class mul
{
public:
    mul(int p1, int p2)
    {
        param1 = p1;
        param2 = p2;
    }
    operator int ()
    {
        return param1 * param2;
    }

    operator std::string ()
    {
        return std::string(param2, param1 + '0');
    }

private:
    int param1;
    int param2;
};

我不会用那个。

【讨论】:

  • 您可以添加转换函数,如operator identity&lt;int(*)()&gt;::type() { return &amp;operator int; },它允许将其传递给采用int(*)() 的函数,如果调用函数指针,则调用operator int 函数。
【解决方案3】:

如果你想让mul 成为一个真正的函数而不是一个类,你可以只使用一个中间类:

class StringOrInt
{
public:
    StringOrInt(int p1, int p2)
    {
        param1 = p1;
        param2 = p2;
    }
    operator int ()
    {
        return param1 * param2;
    }

    operator std::string ()
    {
        return std::string(param2, param1 + '0');
    }

private:
    int param1;
    int param2;
};

StringOrInt mul(int p1, int p2)
{
    return StringOrInt(p1, p2);
}

这让您可以做一些事情,比如将mul 作为函数传递给标准算法:

int main(int argc, char* argv[])
{
    vector<int> x;
    x.push_back(3);
    x.push_back(4);
    x.push_back(5);
    x.push_back(6);

    vector<int> intDest(x.size());
    transform(x.begin(), x.end(), intDest.begin(), bind1st(ptr_fun(&mul), 5));
    // print 15 20 25 30
    for (vector<int>::const_iterator i = intDest.begin(); i != intDest.end(); ++i)
        cout << *i << " ";
    cout << endl;

    vector<string> stringDest(x.size());
    transform(x.begin(), x.end(), stringDest.begin(), bind1st(ptr_fun(&mul), 5));
    // print 555 5555 55555 555555
    for (vector<string>::const_iterator i = stringDest.begin(); i != stringDest.end(); ++i)
        cout << *i << " ";
    cout << endl;

    return 0;
}

【讨论】:

    【解决方案4】:

    没有。

    你不能通过返回值重载,因为调用者可以用它做任何事情(或什么都不做)。考虑:

    mul(1, 2);

    返回值只是被扔掉了,所以不可能仅仅根据返回值来选择重载。

    【讨论】:

    • 事实上你可以这样做,当返回值被忽略时,不需要做任何工作。
    【解决方案5】:

    在中间类中使用隐式转换。

    class BadIdea
    {
      public:
        operator string() { return "silly"; }
        operator int() { return 15; }
    };
    
    BadIdea mul(int, int)
    

    你明白了,不过这个想法很糟糕。

    【讨论】:

      【解决方案6】:

      设 mul 是一个类,mul(x, y) 是它的构造函数,并重载一些强制转换运算符。

      【讨论】:

        【解决方案7】:

        您不能仅根据返回值重载函数。

        但是,虽然严格来说这不是一个重载函数,但您可以从函数中返回一个重载转换运算符的类的实例。

        【讨论】:

          【解决方案8】:

          我想你可以让它返回一些奇怪的类型 Foo,它只捕获参数,然后 Foo 有一个隐式运算符 int 和运算符 string,它会“工作”,虽然它不会真正重载,而是一个隐式转换技巧。

          【讨论】:

            【解决方案9】:

            简而言之,答案是否定的。在 C++ 中,要求是:

            1:函数名称必须相同
            2:参数集必须不同
            *返回类型可以相同也可以不同

            //This is not valid
                int foo();
                float foo();
            
                typedef int Int;
            
                int foo(int j);
                int foo(Int j);
            
            //Valid:
               int foo(int j);
               char* foo(char * s);
               int foo(int j, int k);
               float foo(int j, float k);
               float foo(float j, float k);
            

            【讨论】:

              【解决方案10】:

              据我所知,你不能(可惜……)。作为一种解决方法,您可以改为定义一个“out”参数,然后重载该参数。

              【讨论】:

                【解决方案11】:

                不在 C++ 中。你在上面的例子中得到的是返回值,它是一个 int 转换成 string 可以理解的东西,很可能是 char。这将是 ASCII 18 或“设备控制 2”。

                【讨论】:

                  【解决方案12】:

                  您可以使用上面的仿函数解决方案。 C++ 不支持除 const 以外的函数。您可以基于 const 重载。

                  【讨论】:

                    【解决方案13】:

                    您可以使用模板,但您必须在调用时指定模板参数。

                    【讨论】:

                      【解决方案14】:

                      把它放在不同的命名空间?我会这样做。严格来说不是重载,而是只有两个具有相同名称但范围不同的方法(因此是 :: 范围解析运算符)。

                      所以 stringnamespace::mul 和 intnamespace::mul。也许它不是你真正想要的,但它似乎是唯一的方法。

                      【讨论】:

                        【解决方案15】:

                        你可以做类似的事情

                        template<typename T>
                        T mul(int i,int j){
                            return i * j;
                        }
                        
                        template<>
                        std::string mul(int i,int j){
                            return std::string(j,i);
                        }
                        

                        然后这样称呼它:

                        int x = mul<int>(2,3);
                        std::string s = mul<std::string>(2,3);
                        

                        没有办法重载返回值。

                        【讨论】:

                          【解决方案16】:

                          好的,天才们;)这就是你像专业人士一样做的事情。

                          class mul { int m_i,m_j; public: mull(int i,int j):m_i(i),m_j(j){} template operator R() { return (R)m_i * m_j; } };

                          使用喜欢

                          
                          double d = mul(1,2);
                          long l = mul(1,2);
                          

                          不傻

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 2022-07-25
                            • 2011-03-09
                            • 2016-03-16
                            • 2012-04-02
                            • 2016-09-30
                            相关资源
                            最近更新 更多