将带有可选模板类型参数的函数传递给类构造函数,并将其分配给一个方法。

6

我有两个文件:main.cppcrypto.h,其中包含模板类Crypto。在类构造函数中,我需要传递一个函数指针并将其分配给(*keyGen)方法。我需要传递的函数有一个可选参数。

template <class keyType>
class Crypto {
    private:
        keyType (*keyGen)(keyType);

    public:
        Crypto(keyType (*keyGen)(keyType)) {
            this->keyGen = keyGen;
        }

        void decode() {
            keyType foundKey;

            vector<string> output;
            keyType curKey = keyGen(); // Here keyGen has no args

            // if curKey does not decode the input
                curKey = keyGen(curKey); // Here keyGen has 1 arg of type keyType
            // else foundKey = curKey;

            // Save the decoded file
        }
};

在 main.cpp 中
int keyGen(int key = -1) { // This is the optional param
    key++;
    return key;
}

int main() {
    // ...
    Crypto<int> crypto(keyGen);
    crypto.decode();
}

我需要对解码方法进行修改,使其能够在不传递keyType参数和传递keyType参数两种情况下都能调用keyGen方法。如果使用没有参数的keyGen方法,则返回0;否则返回key+1。 我尝试过对keyGen函数进行重载,但由于它是一个函数指针,所以这是不可能的。我进行了大量研究,但并未找到解决方案。
4个回答

3
如果你想同时使用keyGen()keyGen(curKey),那么keyGen必须是一个带有默认参数的函数本身。请注意,签名中的默认值不会改变该签名 - 因此,指向int keyGen(int key = -1)的指针必须是int(*)(int)类型。
你可以添加两个同名的成员函数,它们内部调用由keyGen指向的函数,并传递正确的参数。
template <class keyType>
class Crypto {
    ...
    void decode() {
        keyType foundKey;

        vector<string> output;
        keyType curKey = keyGenInternal(); // Here keyGen has no args

        // if curKey does not decode the input
        curKey = keyGenInternal(curKey); // Here keyGen has 1 arg of type keyType
        // else foundKey = curKey;
        // Save the decoded file
    }

private:
    keyType keyGenInternal() {
        return keyGen(-1);
    }

    keyType keyGenInternal(keyType key) {
        return keyGen(key);
    }

};

但是,为了使其成为一个适当的解决方案,您需要知道键的默认值 - 您不能仅仅将-1放在那里,因为对于不同类型的键,此值也可能不同。但是,您可以将此值作为另一个模板参数:

template <class keyType, keyType defaultKey>
class Crypto {
    private:
        keyType keyGenInternal() {
            return keyGen(defaultKey);
        }

        keyType keyGenInternal(keyType key) {
            return keyGen(key);
        }
};

int main() {
    // ...
    Crypto<int, -1> crypto(keyGen);
    crypto.decode();
}

或者将其传递给构造函数并存储为成员变量:
template <class keyType>
class Crypto {
    private:
        keyType defaultKey;

    public:
        Crypto(keyType (*keyGen)(keyType), keyType default) : defaultKey(default) {
        this->keyGen = keyGen;
    }
};

int main() {
    // ...
    Crypto<int> crypto(keyGen, -1);
    crypto.decode();
}

我认为这是最直接的解决方案。

最后我使用了构造函数版本。我希望通过指针传递函数可以跟踪默认参数值,但我发现这是不可能的。感谢您的答案。 - Damiano Scevola

3
您可以使用函数对象(functor),即定义了operator()的类。然后可以保存您的默认值。
#include <vector>
#include <string>
#include <iostream>

struct KeyGenerator {
    using key_type = int;
    key_type operator() (key_type key = -1) {
        // If you have a key_gen() already implemented, just call it here
        // like: key_gen(key);
        // Here the random return value is only for demo purpose.
        return key == -1 ? 44444 : 12345 ;
    }
};

template <class KeyGen>
struct Crypto {
    Crypto(KeyGen keygen) : keygen_(keygen) {}

    void decode() {
        std::cout << keygen_() << "\n";
        std::cout << keygen_(2) << "\n";
    }

private:
    KeyGen keygen_;
};

int main() {
    Crypto<KeyGenerator> cry{ KeyGenerator{} };
    cry.decode();
}

OP说:**在类构造函数中,我需要传递一个函数指针[...]。我需要传递的函数有一个可选参数[...]**。对于我来说,很明显,keygen函数可以是一个外部函数 - 你目前的解决方案需要将整个密钥生成逻辑复制粘贴到一个函数对象中,这违反了假设。也许KeyGenerator应该能够调用指定的函数,而不是重新实现它的逻辑..? - Mateusz Grzejek
说实话,这只是一个演示如何使用函数对象来保存默认值的演示程序return 44444; 只是为了在最小化的示例中查看输出。如果 OP 想要使用我的方法,他们应该通过调用自己的 keyGen 函数来替换 operator() 的主体。只需要复制参数列表和默认值。需要复制内部逻辑。 - llllllllll
@MateuszGrzejek 如果您认为我的话有道理,请考虑取消踩。一些评论已添加到答案中。 - llllllllll

2

使用 函数对象 的另一种方式是在类模板内部将其实例化为成员。

template <class T>
struct Foo
{

    Foo( T defaultVal )
        :def(defaultVal)
    {

    }
    T operator()() const
    {
        /* some without param logic */
        return def - 1;
    }

    T operator()( T param ) const
    {
        /* some with param logic */
        return param;
    }

    private:
    T def;

};

template <class keyType>
class Crypto {
    private:
    Foo<keyType>  keyGen;
    public:
        Crypto( keyType defaultVal ) 
            :keyGen(defaultVal)
        {

        }

        void decode() {
            keyType foundKey;

            vector<string> output;
            keyType curKey = keyGen(); // Here keyGen has no args

            // if curKey does not decode the input
                curKey = keyGen(curKey); // Here keyGen has 1 arg of type keyType
            // else foundKey = curKey;

            // Save the decoded file
        }
};

然后,
int main() {
    // ...
    Crypto< int > crypto (-1) ;
    crypto.decode();
}

2

由于默认参数不参与函数签名,所以无法使用它们调用函数指针。

为了解决这个问题,您可以创建一个类,将 KeyGen 函数及其默认参数封装起来:

// wrap key generator in its own class, which you will be able to extend further
template <class Key>
struct KeyGen {
    using KeyGenCallable = Key (*)(Key);
public:
    constexpr KeyGen(KeyGenCallable kg, Key df) : 
      keyGen(kg), defaultKey(df) {}

    const Key& getDefaultKey() {
        return defaultKey;    
    }

    Key operator()() { // uses the default argument
        return (*keyGen)(defaultKey);
    }

    Key operator()(Key k) { // uses the argument you pass to it
        return (*keyGen)(k);
    }

private: 
    KeyGenCallable keyGen;
    Key defaultKey;
};

然后在代码中使用它,而不是在所有地方处理裸函数指针:

// same as before, but instead of receiving a bare function pointer, receives a KeyGen object that wraps it
template <class Key>
class Crypto {
    KeyGen<Key> keyGen;
    public:
        Crypto(KeyGen<Key> kg) : keyGen(kg) {}

        void decode() {
            Key foundKey;

            std::vector<std::string> output;
            Key curKey = keyGen(); // Here keyGen has no args

            if(true) {
                foundKey = keyGen(curKey);
            }   
            else {
                foundKey = curKey;   
            }
            std::cout << foundKey << "\n";
        }
};

int keyGenFuncInt(int key = -1) {
    key++;
    return key;
}

float keyGenFuncFloat(float key = -2.f) { // example of another keygen function
    key *= 2.f;
    return key;
}

int main() {
    KeyGen<int> keygen{ keyGenFuncInt, -1 };
    KeyGen<float> keygen2{ keyGenFuncFloat, -2.f }; // example of another keygen func

    Crypto<int> crypto(keygen);
    crypto.decode();
}

你可以在这里查看一个实时示例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接