简体   繁体   English

C ++模板:类型名和映射到int的函数

[英]C++ Template: typename and function to map to int

I'm writing a C++ template that needs two params: typename T , and an arbitrary function that maps T to an unsigned int . 我正在编写一个需要两个参数的C ++模板: typename T和一个将T映射到unsigned int的任意函数。

How can I declare and use a template that can do that? 如何声明和使用可以做到这一点的模板? I'd like to keep it simple, so that any dumb function can be used. 我想保持简单,以便可以使用任何哑函数。

UPDATE : 更新

Here is an example of what I'd like to do: 这是我想做的一个例子:

 template<typename T, function f> // f has signature: unsigned int f(T);
 class SortedContainer {
      ...
 }

And, in this file: 并且,在此文件中:

 unsigned int weight(Package p) { return p.w; }

 SortedContainer<Package, &weight> sc;

UPDATE upon writing code 编写代码时更新

Based on the answers, I tried writing code, but it won't compile. 根据答案,我尝试编写代码,但无法编译。 Or rather, the template will compile, but not the test which invokes it. 或更确切地说,模板将编译,但不会编译调用它的测试。

The template code looks like this: 模板代码如下所示:

template<typename T, typename f>
class C {
...f(T)...
...

The invocation code looks like: 调用代码如下:

struct S {
int operator()(const int n) {
    return n; // Dummy test code
}
 };

 ...C<int, S>&...

The error message is: 错误消息是:

 error: no matching function for call to 'S::S(const int&)'
 note: candidates are:
 note: S::S()

It seems like it's trying to use S's constructor for some reason, as opposed to using the operator() which I want it to do. 似乎出于某种原因试图使用S的构造函数,而不是使用我想要它执行的operator()

The purpose of the f parameter is that the SortedContainer needs to be able to position T by an integer value. f参数的目的是SortedContainer需要能够以整数值定位T。 T is not necessarily an integer or even Comparable, so the caller, when instantiating a SortedContainer, needs to pass not only type T, but a function f to transform T to an integer. T不一定是整数,甚至不一定是Comparable,因此调用者在实例化SortedContainer时不仅需要传递类型T,还需要传递函数f来将T转换为整数。

The common way of doing this is to accept a general type F for the function. 常用的方法是接受该函数的通用类型F This will allow any kind of function-like object, whether it is a function pointer or a class object with an overloaded operator() . 这将允许任何类型的类似于函数的对象,无论是函数指针还是带有重载operator()的类对象。 So: 所以:

template<class T, class F>
class SortedContainer {
    // ...
}

Compare with things like std::map which does exactly this. 与之类似的std::map进行比较。

The disadvantage of this is that you cannot control what the prototype of the function is. 这样做的缺点是您无法控制该函数的原型。 This may or may not be a problem. 这可能是问题,也可能不是问题。 One way is just to use it as if it was T -to- unsigned int and rely on the fact that the type system will catch any errors at the point of use. 一种方法就是使用它,就好像它是T unsigned int并依赖于类型系统在使用时会捕获任何错误的事实。

Another way would be to verify the constraint with some kind of type trait. 另一种方法是使用某种类型特征来验证约束。 An example: 一个例子:

static_assert(std::is_same<unsigned int,
                           typename std::result_of<F(T)>::type>::value,
              "Function must be T-to-unsigned int");

Edit: I wrote a small example to convince myself i got the assert right, might as well post it. 编辑:我写了一个小例子,以说服自己我正确地主张了,不妨张贴它。 Here, using A will compile OK but B will fail the assertion. 在这里,使用A将编译OK,但是B将使断言失败。

#include <type_traits>

template<class T, class F>
class SortedContainer {
    static_assert(std::is_same<unsigned int,
                               typename std::result_of<F(T)>::type>::value,

                  "Function must be T-to-unsigned int");
};

struct A {
    unsigned int operator()(double) { return 0; }
};
struct B {
    double operator()(double) { return 0; }
};

int main() {
    SortedContainer<double, A> a;
    SortedContainer<double, B> b;
}

Based on your other edit: 根据您的其他编辑:

Note that the templated type F only captures the type of the function. 请注意,模板化类型F仅捕获函数的类型 You still need an object of this type - the actual function - to call. 您仍然需要这种类型的对象 -实际函数-来调用。 Again, compare with std::map which first is templated to take a comparator type, and then has a constructor that takes an object of this type. 再次,与std::map比较,后者首先被模板化以采用比较器类型,然后具有构造器以采用该类型的对象。 This is true even if you use a normal function - the type will be SortedContainer<T, unsigned int (*)(T)> , but you would somehow need to pass the actual function pointer into the container (probably through the constructor). 即使您使用普通函数也是如此-类型将为SortedContainer<T, unsigned int (*)(T)> ,但是您将需要以某种方式将实际的函数指针传递到容器中(可能通过构造函数)。

Something like this: 像这样:

template<class T, class F>
class SortedContainer {
public:
    SortedContainer(F f = F()): func(f) {}

    void foo() {
        // ...
        func();
        // ...
    }
private:
    F func;
};

struct A {
    unsigned int operator()() { return 0; }
};

int main() {
    A a;
    SortedContainer<double, A> c(a);
    c.foo();
}

It's as you said, pretty much: 就像您说的那样:

template< typename T, unsigned int f(T) >
struct SortedContainer;
...
SortedContainer<Package, weight> sc;

if you actually wanted the argument to be a function pointer rather than a function, 如果您实际上希望参数是函数指针而不是函数,

template< typename T, unsigned int (*f)(T) >

and similarly if you want the argument to be a function reference. 同样,如果您希望参数作为函数引用。

(naturally, this will only work for dumb functions , not for function objects with an operator() operator of the right signature) (自然,这仅适用于哑函数 ,不适用于具有正确签名的operator()运算符的函数对象)

You may use C-style function pointers as @Hurkyl suggests, or std::function which probably can't be template parameters, but I think that idea is wrong. 您可以按照@Hurkyl的建议使用C样式的函数指针,也可以使用std::function ,它们可能不能作为模板参数,但是我认为这种想法是错误的。

C++ templates are duck-typed, so STL code in many places ( std::unordered_map -> std::hash , std::sort -> std::less ) relies on that. C ++模板是鸭子类型的,因此许多地方的STL代码( std::unordered_map > std::hashstd::sort > std::less )都依赖于此。 I think you should also apply this approach - just ask user to provide specialization for type T: 我认为您也应该采用这种方法-只是要求用户为T型提供专门化:

/* Universal implementation */
template<typename T>
unsigned int sorted_container_weight(T t) { return t; }

template<typename T>
class SortedContainer {
    T t;
public:
    unsigned int somefunc() {
        return sorted_container_weight(t);
    }    
};

template<>
unsigned int sorted_container_weight<Package>(Package p) { return p.w; }

SortedContainer<Package> sc;

IMO, you don't require a separate template argument for Function F . IMO,您不需要为Function F使用单独的模板参数。

template<typename T>  // F not required!
class SortedContainer {
      ...
};

Choose a good name and use that function by overloading it for various cases. 选择一个好名字,并通过在各种情况下重载它来使用该功能。 eg to_uint() 例如to_uint()
Since you want to map (ie relate) a type to an unsigned int ( uint ), use following function in global scope: 由于您要将类型映射(即关联)到unsigned intuint ),因此请在全局范围内使用以下函数:

template<typename T>
uint to_uint (const T& t) {
  return t.to_uint();  // Must have `uint to_uint() const` member, else error
}

// Overloads of `to_uint()` for PODs (if needed) 

template<typename T>  // For all kinds of pointers
uint to_uint (const T* const pT) {
  if(pT == nullptr)
    <error handling>;
  return to_uint(*pT);
}

Scenario : For Sorted_Container<X> , whenever to_uint(x) is invoked, then: 场景 :对于Sorted_Container<X> ,只要调用to_uint(x) ,则:

  1. If X is a class, then it must have uint to_uint() const method 如果X是一个类,则它必须具有uint to_uint() const方法
  2. Else if X is a POD, then you may have to overload to_uint() for that type 否则,如果X是POD,则可能必须为该类型重载to_uint()
  3. Else, the compiler will generate an error 否则,编译器将生成错误

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM