3

我有一个函数,它将函数指针作为参数,然后用它自己的参数调用该函数:

typedef int (*my_func_ptr)( int );

int foo( my_func_ptr f ) {
    static int i = 0;
    return i = f( i );
}

有时,我需要将函数传递给foo不仅依赖于整数输入的函数来输出结果。

int add_strlen( int i, const char* s ) {
    return i + strlen( s );
}

我可以重新编写上面的代码来使用std::function然后使用std::bind,但我最好在编译时创建这些函数,所以我使用模板。

template<const char* S>
int add_strlen( int i ) {
    return i + strlen( S );
}

/**
 * Usage:
 * char bar[] = "bar";
 * foo( add_strlen<bar> );
 */

使用指针作为模板参数时出现了我的问题。每当我使用指向任何类型的常量数据的指针作为模板参数时,它只有在传递的参数被声明为该类型的非常量数组时才能编译。

char char_array[]             = "works";
const char const_char_array[] = "error";
char *char_ptr                = "error";
const char *const_char_ptr    = "error";

Clang (ver. 3.0-6) 中的相关错误(char_ptr和的错误const_char_ptr是相同的):

func_ptr.cpp:29:9: error: no matching function for call to 'foo'
        foo( add_strlen<const_char_array> );
        ^~~
func_ptr.cpp:6:5: note: candidate function not viable: no overload of 'add_strlen' matching 'my_func_ptr' (aka 'int (*)(int)') for 1st argument
int foo( my_func_ptr f )

谁能向我解释这是为什么?在我看来,模板参数S应该是 type const char*,这在任何其他情况下意味着我可以传入任何 const 或非 const 指针或类型的数组char并期望它工作。我希望能够将我的数组声明为const,因为我什至不想暗示它们应该在运行时进行修改。有没有办法让我的数组保持不变并将它们用作模板参数?

编辑:感谢一些帮助(以及更新版本的 Clang 错误更好),我能够确定提供具有内部链接的模板参数是问题的一部分。通过将上述变量声明为 extern,我可以毫无错误地使用add_strlen<const_char_array>。我还创建了一个简化的测试用例。它包括在下面:


#include <cstring>

typedef int (*my_func_ptr)( int );

int foo( my_func_ptr f ) {
    static int i = 0;
    return i = f( i );
}

template<const char* S>
int add_strlen( int i ) {
    return i + strlen( S );
}

extern char char_array[];
extern const char const_char_array[];
extern char *char_ptr;
extern const char *const_char_ptr;

char char_array[] = "foo";
const char const_char_array[] = "bar";
// assigning to string literal is deprecated
char *char_ptr = char_array;
const char *const_char_ptr = "baz";

int main(int argc, const char *argv[])
{
    foo( add_strlen<char_array> ); // works
    foo( add_strlen<const_char_array> ); // works
    //foo( add_strlen<char_ptr> ); // doesn't work
    //foo( add_strlen<const_char_ptr> ); // doesn't work
    return 0;
}
4

1 回答 1

1

该错误似乎与您是什么以及不允许将其用作非类型模板参数有关,请参阅IBM Linux Compilers 文档中的非类型模板参数,他们有这样的说法:

非类型模板参数的语法与以下类型之一的声明相同:

  • 积分或枚举
  • 指向对象的指针或指向函数的指针
  • 引用对象或引用函数
  • 指向成员的指针

char_array[]传入和工作的原因const_char_array[]是因为它们在编译时是恒定的,并且在程序运行时永远不会在程序下面改变。可以传入整型,但不能传入指向整型的指针。

模板期待const char *aka的类型const char[x],但它也期待永远不会改变的东西,所以指针指向的位置可能永远不会改变。在编译器时传入时,您const_char_array将传递一个char[6](“错误”)。位置永远不会改变,内容永远不会改变。但是,当传入const_char_ptr它时,它会得到一个const char *,虽然指针本身可能永远不会改变,但它指向的位置完全有可能会改变。它本身不是静态的。

char *_arr = new char[20];
const char* _ptr_arr = _arr;

我们可以在这里同意 my_ptr_arr与 your 的类型完全相同const_char_ptr,但存储内容的位置可能会在运行时发生变化。在不允许的模板中,因为它可能需要模板的全新实例化,并且从创建模板时开始就不确定。Achar [6]是静态的,不会改变。

foo( add_strlen<_ptr_arr> );

导致以下编译器错误:

test.cpp:36:5: error: no matching function for call to 'foo'
    foo( add_strlen<_ptr_arr>);
    ^~~
test.cpp:6:5: note: candidate function not viable: no overload of 'add_strlen' matching 'my_func_ptr' (aka 'int (*)(int)') for 1st argument
int foo( my_func_ptr f ) {
    ^

这不是很有帮助,我们想弄清楚为什么没有有效的重载,使用独立的函数编译代码而不作为函数指针传递,我们得到以下结果:

add_strlen<_ptr_arr>(0);

将导致:

test.cpp:36:5: error: no matching function for call to 'add_strlen'
    add_strlen<_ptr_arr>(0);
    ^~~~~~~~~~~~~~~~~~~~
test.cpp:16:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'S'
int add_strlen( int i ) {
    ^

所以显式指定的参数是无效的,具体来说,我们不能传入指向积分的指针。

于 2013-02-22T00:17:25.837 回答