Search code examples
c++sortingstlcudathrust

Why does thrust::sort for customized data needs a default constuctor while STL::sort does't?


I am trying to sort a set of triangles by minimum x-coordinate using thrust:

struct real3 
{
    float x, y, z;
};

struct triangle
{
    real3 p1, p2, p3;

    float Xmin, Xmax;

    triangle(real3 ip1, real3 ip2, real3 ip3) :p1(ip1), p2(ip2), p3(ip3)
    {
        Xmax = (p1.x > p2.x) ? ((p1.x > p3.x) ? p1.x : p3.x) : ((p2.x > p3.x) ? p2.x : p3.x);
        Xmin = (p1.x < p2.x) ? ((p1.x < p3.x) ? p1.x : p3.x) : ((p2.x < p3.x) ? p2.x : p3.x);
    }
};

bool triangle_less_Xmin(const triangle& f, const triangle& b)
{
    return f.Xmin < b.Xmin;
}

and

thrust::device_vector<triangle> devXXX(XXX);
thrust::sort(devXXX.begin(), devXXX.end(), triangle_less_Xmin);

but at compile time, an error message complained that "no default constructor exists for class tringle". But if I use std::vector for the sort like below:

std::vector<triangle> sss;
std::sort(sss.begin(), sss.end(), triangle_less_Xmin);

Everything gose well. Also, if I add triangle(){} to the struct, thrust compiles well, too. So, what is the problem? As far as I know, sorting doesn't neet to creat new data. Am I supposed to do something special in default constuctor?


Edit 30 minutes later

I found this in thrust source code, though do not really know the exact meaning. This is where the error pointed to:

__host__ inline static result_type host_path(execution_policy<DerivedPolicy> &exec, Pointer ptr)
{
  // when called from host code, implement with assign_value
  // note that this requires a type with default constructor
  result_type result;

  thrust::host_system_tag host_tag;
  cross_system<thrust::host_system_tag, DerivedPolicy> systems(host_tag, exec);
  assign_value(systems, &result, ptr);

  return result;
}

Solution

  • You have both the source of the error and the solution already in your question, and the compiler provided a very informative error message.

    But for the sake of completeness, Thrust is requiring that your triangle class has a default constructor, and although I'm not sure the code you posted pinpoints the exact place in the codebase (or your devXXX is actually a host vector and not a device vector), somewhere thrust is instantiating your class without arguments, causing the failure you see.

    In general, when defining a class with an explicit non-default constructor, you should also provide your own default constructor, because providing any constructor definition disables the compiler's implicit default constructor.

    So this is OK:

    struct triangle
    {
        real3 p1, p2, p3;
    
        float Xmin, Xmax;
    
    };
    
    triangle t;  // Implicit default constructor called
    

    and this is OK

    struct triangle
    {
        real3 p1, p2, p3;
    
        float Xmin, Xmax;
    
        triangle() {};
        triangle(real3 ip1, real3 ip2, real3 ip3) :p1(ip1), p2(ip2), p3(ip3)
        {
            Xmax = (p1.x > p2.x) ? ((p1.x > p3.x) ? p1.x : p3.x) : ((p2.x > p3.x) ? p2.x : p3.x);
            Xmin = (p1.x < p2.x) ? ((p1.x < p3.x) ? p1.x : p3.x) : ((p2.x < p3.x) ? p2.x : p3.x);
        }
    };
    
    triangle  t; // Explicit default constructor called 
    

    but your definition

    struct triangle
    {
        real3 p1, p2, p3;
    
        float Xmin, Xmax;
    
        triangle(real3 ip1, real3 ip2, real3 ip3) :p1(ip1), p2(ip2), p3(ip3)
        {
            Xmax = (p1.x > p2.x) ? ((p1.x > p3.x) ? p1.x : p3.x) : ((p2.x > p3.x) ? p2.x : p3.x);
            Xmin = (p1.x < p2.x) ? ((p1.x < p3.x) ? p1.x : p3.x) : ((p2.x < p3.x) ? p2.x : p3.x);
        }
    };
    
    triangle t; // Syntax error here, no default constructor
    

    is not OK because you have overridden the compiler's default constructor without providing your own. You should regard the fact that your class works with std::sort as an accident, rather than any kind of deficiency in Thrust.