Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ template friend class does not satisfy conceptual constraints because it is an incomplete type

#include <mutex>
#include <shared_mutex>

template<typename T>
concept is_buffer = requires (T t) {
    { t.pointer() } -> std::same_as<char*>;
    { t.capacity() } -> std::same_as<size_t>;
    { t.rwlock() } -> std::same_as<std::shared_mutex&>;
};

template<typename Buffer>
requires is_buffer<Buffer>
class buffer_stream {
};

class shared_array_buffer {
private:
    struct meta {
        char* ptr {};
        size_t capacity {};
        std::mutex mutex {};
        std::shared_mutex rwlock{};
        int reference_count {};
    };
    meta* meta_ {};
    /* Error occured here */
    friend class buffer_stream<shared_array_buffer>;
public:
    char* pointer() {
        return meta_->ptr;
    }
    size_t capacity() {
        return meta_->capacity;
    }
    std::shared_mutex& rwlock() {
        return meta_->rwlock;
    }
};
int main() {
    shared_array_buffer buffer;
    buffer_stream<shared_array_buffer> stream;
}

In this code, I use the class buffer_stream as the friend class of shared_array_buffer and unique_array_buffer, they are all template class with concept.

The compiler says template constraint failure for ‘template<class Buffer> requires is_buffer<Buffer> class buffer_stream’ because ‘t’ has incomplete type(in class shared_array_buffer).

I wonder why the shared_array_buffer is an incomplete type.All the types of the member is fully defined.

like image 279
gxglous Avatar asked Nov 01 '25 19:11

gxglous


1 Answers

friend class buffer_stream<shared_array_buffer>;

This makes the is_buffer concept check if shared_array_buffer is valid, but shared_array_buffer is not complete that the point of the friend declaration, so compilation fails.

A workaround could be to delay the validation using a static_assert instead:

template <typename Buffer>
class buffer_stream {
    static_assert(is_buffer<Buffer>);
};

Another option is to let buffer_stream inherit from the buffer type. You can then create protected accessors that it can use and don't need to make it a friend:

class shared_array_buffer {
private:
    struct meta {
        char* ptr{};
        size_t capacity{};
        std::mutex mutex{};
        std::shared_mutex rwlock{};
        int reference_count{};
    };
    meta* meta_{};

public:
    char* pointer() { return meta_->ptr; }
    size_t capacity() { return meta_->capacity; }
    std::shared_mutex& rwlock() { return meta_->rwlock; }
};

template <typename Buffer>
    requires is_buffer<Buffer>
class buffer_stream : public Buffer {
};
like image 182
Ted Lyngmo Avatar answered Nov 04 '25 09:11

Ted Lyngmo