【问题标题】:Friend function not found by inner class in Type Erasure setup C++Type Erasure setup C++中的内部类找不到Friend函数
【发布时间】:2021-12-17 00:52:24
【问题描述】:

我观看了 Klaus Iglberger 在 CppCon 2021 上关于类型擦除的精彩演讲。我根据他的示例自己设置了模式。他的 External Polymorphic 片段被分派给外部类的友元函数。那篇文章不适合我。

我尝试过改变定义的顺序并将友元函数的实现拉到类之外。我还没有设置实现文件,因为我不太相信这会是一个解决方案。

我使用的是 GCC 9.3 和 Clang 10。错误看起来一样。铿锵10: type_erasure_example.cpp:57:22: error: too many arguments to function call, expected 0, have 1 serialize( object );

type_erasure_example.cpp:61:28: error: too many arguments to function call, expected 2, have 3 draw( object, x, y );

GCC9.3:

type_erasure_example.cpp:57:11: error: no matching function for call to ‘Shape::ShapeModel<Square>::serialize(const Square&) const’ 57 | serialize( object );

type_erasure_example.cpp:61:11: error: no matching function for call to ‘Shape::ShapeModel<Square>::draw(const Square&, int&, int&) const’ 61 | draw( object, x, y );

#include <iostream>
#include <memory>
#include <vector>

class Circle 
{ 
 public: 
   explicit Circle( double rad ) 
      : radius{ rad } 
      // ... Remaining data members 
   {} 
   double getRadius() const noexcept { return radius; }
   // ... getCenter(), getRotation(), ... 
 private: 
   double radius; 
   // ... Remaining data members 
};

class Square 
{ 
 public: 
   explicit Square( double s ) 
      : side{ s } 
      // ... Remaining data members 
   {} 
   double getSide() const noexcept { return side; }
   // ... getCenter(), getRotation(), ... 
 private: 
   double side; 
   // ... Remaining data members 
};

class Shape 
{ 
 private:
    struct ShapeConcept 
    { 
       virtual ~ShapeConcept() {} 
       virtual void serialize() const = 0; 
       virtual void draw(int x, int y) const = 0; 
       virtual std::unique_ptr<ShapeConcept> clone() const = 0; 
    }; 

    template< typename T > 
    struct ShapeModel : ShapeConcept 
    { 
       ShapeModel( T&& value ) 
          : object{ std::forward<T>(value) } 
       {} 
       
       std::unique_ptr<ShapeConcept> clone() const override 
       { 
          return std::make_unique<ShapeModel>(*this); 
       } 
       void serialize() const override 
       { 
          serialize( object ); // Error
       } 
       void draw( int x, int y ) const override 
       { 
          draw( object, x, y ); // Error
       } 
       T object; 
    };

    friend void serialize( Shape const& shape )
    { 
       shape.pimpl->serialize(); 
    }
    friend void draw( Shape const& shape, int x, int y )
    { 
       shape.pimpl->draw( x, y ); 
    }

    std::unique_ptr<ShapeConcept> pimpl; 
 public: 
   template< typename T > 
   Shape( T&& x ) 
      : pimpl{ new ShapeModel<T>( std::forward<T>(x) ) } 
   {} 
   // Special member functions 
   Shape( Shape const& s ); 
   Shape( Shape&& s ); 
   Shape& operator=( Shape const& s ); 
   Shape& operator=( Shape&& s ); 
};


void serialize( Circle const& circle ) {
    std::cout << "Serializing a circle with radius "
        << circle.getRadius() << std::endl;
} 
void draw( Circle const& circle, int x, int y ) {
    std::cout << "Drawing a circle with radius "
        << circle.getRadius() << " Coordinates ("
        << x << ", " << y << ")" << std::endl;
}
void serialize( Square const& square ) {
    std::cout << "Serializing a square with side "
        << square.getSide() << std::endl;
} 
void draw( Square const& square, int x, int y ) {
    std::cout << "Drawing a square with side "
        << square.getSide() << " Coordinates ("
        << x << ", " << y << ")" << std::endl;
} 

void drawAllShapes( std::vector<Shape> const& shapes ) 
{ 
   for( auto const& shape : shapes ) 
   { 
      draw( shape , 0.0, 0.0 ); 
   } 
}
int main() 
{ 
   using Shapes = std::vector<Shape>; 
   // Creating some shapes 
   Shapes shapes; 
   shapes.emplace_back( Circle{ 2.0 } ); 
   shapes.emplace_back( Square{ 1.5 } ); 
   shapes.emplace_back( Circle{ 4.2 } ); 
   // Drawing all shapes 
   drawAllShapes( shapes ); 
}

【问题讨论】:

  • 请将 fullcomplete 错误输出复制粘贴到您的问题中。并在出现错误的代码中添加 cmets。
  • look-up rulevoid serialize() const override { serialize( object );}只创建类的成员(ADL生效前)。
  • 如果您想看另一个示例,因为它有点令人兴奋,您可能需要查看我的答案:stackoverflow.com/a/63743699/4641116。 (我通过将名称更改为 fwd_serialize 让你的代码工作,而你通过::serialize 让它工作。)
  • @Jarod42 为什么查找会停止呢?是不是因为查找集一旦找到ShapeModel::serializa()就不是空的,即使签名不匹配?
  • ADL 还声明 “首先,如果通常的非限定查找生成的查找集包含以下任何内容,则不考虑依赖于参数的查找:1) 类成员的声明".

标签: c++ type-erasure argument-dependent-lookup friend-function


【解决方案1】:

有人在 cmets 上发帖,这让我走上了正轨。我对设置不够了解。朋友类正在得到正确处理。需要重新安排的是免费功能。一个实现文件毕竟会有所帮助,我们可以只做标题:

#include <iostream>
#include <memory>
#include <vector>

class Circle 
{ 
 public: 
   explicit Circle( double rad ) 
      : radius{ rad } 
      // ... Remaining data members 
   {} 
   double getRadius() const noexcept { return radius; }
   // ... getCenter(), getRotation(), ... 
 private: 
   double radius; 
   // ... Remaining data members 
};

class Square 
{ 
 public: 
   explicit Square( double s ) 
      : side{ s } 
      // ... Remaining data members 
   {} 
   double getSide() const noexcept { return side; }
   // ... getCenter(), getRotation(), ... 
 private: 
   double side; 
   // ... Remaining data members 
};

void serialize( Circle const& circle ) {
    std::cout << "Serializing a circle with radius "
        << circle.getRadius() << std::endl;
} 
void draw( Circle const& circle, int x, int y ) {
    std::cout << "Drawing a circle with radius "
        << circle.getRadius() << " Coordinates ("
        << x << ", " << y << ")" << std::endl;
}
void serialize( Square const& square ) {
    std::cout << "Serializing a square with side "
        << square.getSide() << std::endl;
} 

void draw( Square const& square, int x, int y ) {
    std::cout << "Drawing a square with side "
        << square.getSide() << " Coordinates ("
        << x << ", " << y << ")" << std::endl;
} 

class Shape 
{ 
 private:
    struct ShapeConcept 
    { 
       virtual ~ShapeConcept() {} 
       virtual void serialize() const = 0; 
       virtual void draw(int x, int y) const = 0; 
       virtual std::unique_ptr<ShapeConcept> clone() const = 0; 
    }; 

    template< typename T > 
    struct ShapeModel : ShapeConcept 
    { 
       ShapeModel( T&& value ) 
          : object{ std::forward<T>(value) } 
       {} 
       
       std::unique_ptr<ShapeConcept> clone() const override 
       { 
          return std::make_unique<ShapeModel>(*this); 
       } 
       void serialize() const override 
       { 
          ::serialize( object ); 
       } 
       void draw( int x, int y ) const override 
       { 
          ::draw( object, x, y ); 
       } 
       T object; 
    };

    friend void serialize( Shape const& shape )
    { 
       shape.pimpl->serialize(); 
    }
    friend void draw( Shape const& shape, int x, int y )
    { 
       shape.pimpl->draw( x, y ); 
    }

    std::unique_ptr<ShapeConcept> pimpl; 
 public: 
   template< typename T > 
   Shape( T&& x ) 
      : pimpl{ new ShapeModel<T>( std::forward<T>(x) ) } 
   {} 
   // Special member functions 
   Shape( Shape const& s ) = default;
   Shape( Shape&& s ) = default;
   Shape& operator=( Shape const& s ) = default;
   Shape& operator=( Shape&& s ) = default;
};


void drawAllShapes( std::vector<Shape> const& shapes ) 
{ 
   for( auto const& shape : shapes ) 
   { 
      draw( shape , 0.0, 0.0 ); 
   } 
}
int main() 
{ 
   using Shapes = std::vector<Shape>; 
   // Creating some shapes 
   Shapes shapes; 
   shapes.emplace_back( Circle{ 2.0 } ); 
   shapes.emplace_back( Square{ 1.5 } ); 
   shapes.emplace_back( Circle{ 4.2 } ); 
   // Drawing all shapes 
   drawAllShapes( shapes ); 
}

【讨论】:

  • 是的,这是我的评论,但我忽略了底部的免费功能,这就是我再次删除它的原因。我想知道为什么这里需要范围解析,不确定 ADL 何时启动。
【解决方案2】:

感谢您的代码,但我认为您忘记使用 clone() 正确定义 Shape 的复制构造函数和赋值运算符,因此您可以将 Shape 视为 Klaus Iglberger 建议的值。我已经将它与 scale() 函数一起添加到 main() 中进行测试。

#include <iostream>
#include <memory>
#include <vector>

class Circle 
{ 
public: 
    explicit Circle( double rad ) 
        : radius{ rad } 
          // ... Remaining data members 
    {} 
    double getRadius() const noexcept { return radius; }
    void setRadius(double rad) noexcept { radius=rad; }
    // ... getCenter(), getRotation(), ... 
private: 
    double radius; 
    // ... Remaining data members 
};

class Square 
{ 
public: 
    explicit Square( double s ) 
        : side{ s } 
          // ... Remaining data members 
    {} 
    double getSide() const noexcept { return side; }
    void setSide(double s) noexcept { side=s; }
    // ... getCenter(), getRotation(), ... 
private: 
    double side; 
    // ... Remaining data members 
};

void serialize( Circle const& circle ) {
    std::cout << "Serializing a circle with radius "
              << circle.getRadius() << std::endl;
} 
void draw( Circle const& circle, int x, int y ) {
    std::cout << "Drawing a circle with radius "
              << circle.getRadius() << " Coordinates ("
              << x << ", " << y << ")" << std::endl;
}
void scale( Circle& circle, double scale_factor ) {
    circle.setRadius( circle.getRadius() * scale_factor );
}

void serialize( Square const& square ) {
    std::cout << "Serializing a square with side "
              << square.getSide() << std::endl;
} 
void draw( Square const& square, int x, int y ) {
    std::cout << "Drawing a square with side "
              << square.getSide() << " Coordinates ("
              << x << ", " << y << ")" << std::endl;
} 
void scale( Square& square, double scale_factor ) {
    square.setSide( square.getSide() * scale_factor );
}


class Shape 
{ 
private:
    struct ShapeConcept 
    { 
        virtual ~ShapeConcept() {} 
        virtual void serialize() const = 0; 
        virtual void draw(int x, int y) const = 0; 
        virtual std::unique_ptr<ShapeConcept> clone() const = 0;
        virtual void scale(double scale_factor)=0;
    }; 

    template< typename T > 
    struct ShapeModel : ShapeConcept 
    { 
        ShapeModel( T&& value ) 
            : object{ std::forward<T>(value) } 
        {} 
       
        std::unique_ptr<ShapeConcept> clone() const override 
        { 
            return std::make_unique<ShapeModel>(*this); 
        } 
        void serialize() const override 
        { 
            ::serialize( object ); 
        } 
        void draw( int x, int y ) const override 
        { 
            ::draw( object, x, y ); 
        }
        void scale(double scale_factor) override
        {
            ::scale(object,scale_factor);
        }
        T object; 
    };

    friend void serialize( Shape const& shape )
    { 
        shape.pimpl->serialize(); 
    }
    friend void draw( Shape const& shape, int x, int y )
    { 
        shape.pimpl->draw( x, y ); 
    }
    friend void scale( Shape& shape, double scale_factor)
    { 
        shape.pimpl->scale(scale_factor); 
    }
    
    std::unique_ptr<ShapeConcept> pimpl; 
public: 
    template< typename T > 
    Shape( T&& x ) 
        : pimpl{ new ShapeModel<T>( std::forward<T>(x) ) } 
    {} 
    // Special member functions
    Shape( Shape const& s ) : pimpl{ s.pimpl->clone() } {}
    Shape& operator=( Shape const& s ) { this->pimpl = s.pimpl->clone(); return *this; }
    Shape( Shape&& s ) = default;
    Shape& operator=( Shape&& s ) = default;
};

void drawAllShapes( std::vector<Shape> const& shapes ) 
{ 
    for( auto const& shape : shapes ) 
    { 
        draw( shape , 0.0, 0.0 ); 
    } 
}

int main() 
{ 
    using Shapes = std::vector<Shape>; 

    // Creating some shapes 
    Shapes shapes; 
    shapes.emplace_back( Circle{ 2.0 } ); 
    shapes.emplace_back( Square{ 1.5 } ); 
    shapes.emplace_back( Circle{ 4.2 } ); 

    // Drawing all shapes 
    drawAllShapes( shapes );

    // Copying and scaling all shapes
    auto shapes_scaled = shapes; // copy shapes, here clone() is used
    for ( auto& shape : shapes_scaled )
        scale( shape, 10 );
    drawAllShapes( shapes_scaled ); // scaled by factor 10
    drawAllShapes( shapes );        // left untouched
}

【讨论】:

  • 复制部分无法在 VS2019 上编译 - type_erasure.cpp(95,1): error C2665: 'scale': 2 个重载都不能转换所有参数类型
  • 对于这一行 "auto shapres_scaled = shapes" 看起来 GCC 正在为每个元素调用复制构造函数 "Shape(Shape const& s)" 而 clang 和 msvc 调用模板化构造函数 "Shape(T&& x) "
【解决方案3】:

在 Klaus 的帮助下完成示例 我通过在基于@bterwijn post 的模板构造函数上添加特征和约束来修复复制/移动/分配问题

...
class Shape;
template< typename T >
struct IsShape : public std::is_same< Shape, std::decay_t<T> >
{};

...
template<typename T, typename = std::enable_if_t<!IsShape<T>::value>> 
Shape( T&& x ) : pimpl{ new ShapeModel<T>( std::forward<T>(x) ) }  { } 

这是一个完整的工作示例(在 GCC、CLANG 和 MSVC 上测试) https://wandbox.org/permlink/BvOngSRPXtEnFPMR

请注意,没有检查空形状(即 pimpl == nullptr)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-07
    相关资源
    最近更新 更多