开发者

Why is passing this kind of variable-size parameter by value impossible?

开发者 https://www.devze.com 2023-01-26 09:38 出处:网络
We have a structure like below: template<size_t size> class Container{ public: char characters[size];

We have a structure like below:

template<size_t size>
class Container{
 public:
  char characters[size];
};

And we have a function like below:

template <size_t size>
void function(Container<size> input,size_t Size){
 //all instances do exactly same thing and with regard to Size that determines the size of object
}

Now in C++ for every value of size, a different instance of function will be created and apparently that's not right in this case since all instances do the same thing, to avoid this the first function parameter should be a pointe开发者_运维问答r(char * pointer) instead of an object so that accepts any array with any size eliminating the need for function to be templated, but I'm curious having a single function which accepts a variable-size parameter like above that's not allowed by C++ is something impossible at all to implement and generate assembly for, or somehow leads to an inefficient implementation in terms of speed/memory ?


It's common to delegate data that is independent of template arguments to a non-template base class, to avoid code duplication. In this case, the address of the first element of the array is not dependent on any template argument, so you can move it out

class ContainerBase {
 private: // can't be copied
  ContainerBase(ContainerBase const& o);
  ContainerBase &operator=(ContainerBase &);

 protected:
  ContainerBase(char *datap):datap(datap) { }

 public:
  char *data() { return datap; }

 private:
  char *datap;
};

template<std::size_t size>
class Container : public ContainerBase {
 public:
  Container():ContainerBase(d.characters)
  Container(Container const& o):ContainerBase(d.characters), d(o.d) 
  { }

  Container &operator=(Container const& o) {
    d = o.d;
    return *this;
  }

  struct {
   char characters[size];
  } d;
};


void function(ContainerBase const& input, std::size_t Size){
 /* operate on input.data() */
}

In a class small like that it probably wouldn't pay off, but cost/use ratio increases as the independent operations or the class itself grows. Note that if your goal is to have the class as aggregate then the above way is not feasible. You will need to either delegate work to some other non-templated function and live with the (possibly very small anyway) generated template specializations or you just pass the address of the array manually. This would also help making non-template functions more general

template<std::size_t size>
class Container : public ContainerBase {
 public:
  char *data() { return characters; }

 public:
  char characters[size];
};

// note that this function works in plain arrays too
void function(char *input, std::size_t Size){
 /* operate on input */
}

function(c.data(), N);

Incidentally, then you are at boost::array<char, N> which provides exactly that interface and other useful functions (still being an aggregate), although it's a bit too general in that it isn't fixed on char.


Generally, where you need an IN parameter, pass built-in types by value, and other types by reference to const, i.e.,

template< size_t size >
void function( Container<size> const& input,size_t Size )
{
 //all instances do exactly same thing and with regard to Size that determines the size of object
}

With this definition, chances are that the compiler+linker will optimize things so that there will be only one machine code version of function.

I was a bit surprised the first time I checked a simple small example program, that expressing it with compile time polymorphism (templating) produced smaller and more efficient code than expressing it with run time polymorphism!

Try it yourself, and if you're as surprised as I once was, then Good! Otherwise, chances are that there'll be no significant difference. But in some corner case you may find what in the old days was called "template code bloat", and then it's time to ignore it or measure whether it's significant enough to do work on translating to run time polymorphism.

Now to your question,

"I'm curious having a single function which accepts a variable-size parameter like above that's not allowed by C++ is something impossible at all to implement and generate assembly for, or somehow leads to an inefficient implementation in terms of speed/memory?"

No, it's not impossible to transform the compile time polymorphism to efficient run time polymorphism, or to simply no polymorphism. Especially since you're already passing a run-time size (which presumably is guaranteed smaller than the fixed size).

A safe way is to use std::string from the C++ standard library, header <string>. That involves dynamic allocation somewhere in the internals of std::string, done automatically for you, but affecting efficiency. But your code, containing char[size] characters, was not valid C++, and that indicates beginner level, so chances are that your design was not chosen for any good reason -- hence, do:

class Container
{
public:
    std::string characters;
};

void function( Container const& input )
{
    // whatever, using e.g. input.characters.length()
}

Cheers & hth.,


One possible solution is to have the size-templated function delegate the real work to an ordinary function that takes the size as an ordinary parameter:

#include <cstddef>
#include <cstdio>

template <size_t size>
struct Container
{
    char characters[size];
};

template <size_t size>
inline void function(const Container<size>& input)
{
    real_work(input.characters, size);
}

void real_work(const char* p, size_t len)
{
    while (len--) puts(p++);
}

int main()
{
    Container<6> x = {"hello"};
    function(x);
}

Note that I deliberately named the different kinds of arguments differently so you don't get confused.


Container<1> is a different type than Container<2>, and passing a pointer won't solve you anything, because they'll be pointer to different types.

Don't you think that size should be a Container's constructor parameter, not a templated value?


Its a poor design anyway. Passing arrays by value, or structs or classes for that matter, is extremely inefficient in both time and space anyway, as the entire array has to be copied into the target stack frame. Pass a reference.


Would something like this be acceptable for you:

class BaseContainer{ /* ... */ };

template<size_t size>
class Container : public BaseContainer{
 public:
  char[size] characters;
};

void function(BaseContainer input,size_t Size){
  //
}
0

精彩评论

暂无评论...
验证码 换一张
取 消